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/main.js CHANGED
@@ -28,9 +28,12 @@ import {
28
28
  formatChallengeAnalysis,
29
29
  formatTurnRecord,
30
30
  getApiKey,
31
+ getGlobalRequestCount,
32
+ getGlobalTokenUsage,
31
33
  getModel,
32
34
  getNextTurnNumber,
33
35
  getServiceContext,
36
+ getShellSupervisorLifecycleSnapshot,
34
37
  getTimeAdaptiveStrategy,
35
38
  parseTurnNumbers,
36
39
  readJournalSummary,
@@ -39,7 +42,7 @@ import {
39
42
  rotateTurnRecords,
40
43
  setCurrentTurn,
41
44
  writePolicyDocument
42
- } from "./chunk-BGEXGHPB.js";
45
+ } from "./chunk-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-KBJPZDIL.js";
85
+ } from "./chunk-UB7RW6LM.js";
83
86
  import {
84
87
  EXIT_CODES,
85
88
  getPipelineConfig,
86
89
  getPromptBuilderConfig,
87
90
  getPromptSources
88
- } from "./chunk-YFDJI3GO.js";
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 useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef3 } from "react";
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-VFIOGTRC.js");
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(state, events, toolRegistry, approvalGate, scopeGuard) {
1751
- super(AGENT_ROLES.ORCHESTRATOR, state, events, toolRegistry);
1752
- this.approvalGate = approvalGate;
1753
- this.scopeGuard = scopeGuard;
1754
- this.promptBuilder = new PromptBuilder(state);
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
- return this.promptBuilder.build(this.userInput, this.state.getPhase() || PHASES.RECON, memory);
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(state, events, toolRegistry, approvalGate, scopeGuard) {
3083
+ static build(options) {
1956
3084
  const cfg = getPipelineConfig();
1957
3085
  _YamlRuntime.setupWorkspace(cfg.workspace);
1958
- return new MainAgent(state, events, toolRegistry, approvalGate, scopeGuard);
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
- var useAgentState = (isTyping = false) => {
2190
- const [messages, setMessages] = useState([]);
2191
- const [isProcessing, setIsProcessing] = useState(false);
2192
- const [currentStatusState, setCurrentStatusState] = useState("");
2193
- const [elapsedTime, setElapsedTime] = useState(0);
2194
- const [retryState, setRetryState] = useState({ status: "idle" });
2195
- const [currentTokens, setCurrentTokens] = useState(0);
2196
- const [inputBroker, setInputBroker] = useState(
2197
- createInputRequestBrokerState()
2198
- );
2199
- const [stats, setStats] = useState({ phase: DEFAULTS.INIT_PHASE, targets: 0, findings: 0, todo: 0, targetLabel: "" });
2200
- const [turnCount, setTurnCount] = useState(0);
2201
- const [liveProgressState, setLiveProgressState] = useState(DEFAULT_LIVE_PROGRESS);
2202
- const startTimeRef = useRef(0);
2203
- const inputBrokerRef = useRef(createInputRequestBrokerState());
2204
- inputBrokerRef.current = inputBroker;
2205
- const retryCountdownRef = useRef(null);
2206
- const retryCountRef = useRef(0);
2207
- const tokenAccumRef = useRef(0);
2208
- const lastStepTokensRef = useRef(0);
2209
- const toolStartedAtRef = useRef(0);
2210
- const addMessage = useCallback((type, content) => {
2211
- const id = generateId();
2212
- setMessages((prev) => {
2213
- const next = [...prev, { id, type, content, timestamp: /* @__PURE__ */ new Date() }];
2214
- if (next.length > TUI_DISPLAY_LIMITS.MAX_MESSAGES) {
2215
- return next.slice(next.length - TUI_DISPLAY_LIMITS.MAX_MESSAGES);
2216
- }
2217
- return next;
2218
- });
2219
- }, []);
2220
- const setCurrentStatus = useCallback((value) => {
2221
- setCurrentStatusState(truncate(value, TUI_DISPLAY_LIMITS.STATUS_TEXT_MAX));
2222
- }, []);
2223
- const setLiveProgress = useCallback((value) => {
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
- const resetCumulativeCounters = useCallback(() => {
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(DEFAULT_LIVE_PROGRESS);
2235
- }, []);
2236
- const manageTimer = useCallback((action) => {
2237
- if (action === "start") {
2238
- startTimeRef.current = Date.now();
2239
- setElapsedTime(0);
2240
- } else {
2241
- const startedAt = startTimeRef.current;
2242
- if (startedAt > 0) {
2243
- setElapsedTime(Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)));
2244
- } else {
2245
- setElapsedTime(0);
2246
- }
2247
- }
2248
- }, []);
2249
- const clearAllTimers = useCallback(() => {
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: liveProgressState,
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 useRef2 } from "react";
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 = useRef2("");
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] = useState2(() => createMainAgent(shouldAutoApprove));
3080
- const eventsRef = useRef3(agent.getEventEmitter());
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] = useState2([]);
3106
- const messageQueueLengthRef = useRef3(0);
3107
- const setMessageQueueSafe = useCallback2((value) => {
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 = useRef3(false);
3145
- const executeTask = useCallback2(async (task) => {
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 = useCallback2(() => {
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 = useCallback2(() => {
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 = useRef3(inputRequest);
4374
+ const inputRequestRef = useRef6(inputRequest);
3205
4375
  inputRequestRef.current = inputRequest;
3206
- const cancelInputRequest = useCallback2(() => {
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 = useCallback2(() => {
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 useCallback3, useMemo } from "react";
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 = useCallback3(async (cmd, args) => {
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 useCallback6, useEffect as useEffect4 } from "react";
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 useRef4, useCallback as useCallback4, useEffect as useEffect3 } from "react";
5015
+ import { useRef as useRef7, useCallback as useCallback8, useEffect as useEffect3 } from "react";
3846
5016
  var useDoubleTap = ({ onFirstTap, onSecondTap, windowMs }) => {
3847
- const timerRef = useRef4(null);
3848
- const pressedRef = useRef4(false);
3849
- const trigger = useCallback4(() => {
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 = useCallback4(() => {
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 useCallback5 } from "react";
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 = useCallback5(() => {
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(useCallback6((ch, key) => {
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 useState3, useCallback as useCallback7, useRef as useRef5 } from "react";
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] = useState3("");
4009
- const [secretInput, setSecretInput] = useState3("");
4010
- const [autoApproveMode, setAutoApproveMode] = useState3(autoApprove);
4011
- const [modal, setModal] = useState3({ type: null, content: "", scrollOffset: 0 });
4012
- const [footerConfig, setFooterConfig] = useState3(DEFAULT_FOOTER_DISPLAY_CONFIG);
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 = useRef5(isProcessing);
5208
+ const isProcessingRef = useRef8(isProcessing);
4039
5209
  isProcessingRef.current = isProcessing;
4040
- const autoApproveModeRef = useRef5(autoApproveMode);
5210
+ const autoApproveModeRef = useRef8(autoApproveMode);
4041
5211
  autoApproveModeRef.current = autoApproveMode;
4042
- const inputRequestRef = useRef5(inputRequest);
5212
+ const inputRequestRef = useRef8(inputRequest);
4043
5213
  inputRequestRef.current = inputRequest;
4044
- const isModalOpenRef = useRef5(!!modal.type);
5214
+ const isModalOpenRef = useRef8(!!modal.type);
4045
5215
  isModalOpenRef.current = !!modal.type;
4046
- const inputRef = useRef5(input);
5216
+ const inputRef = useRef8(input);
4047
5217
  inputRef.current = input;
4048
- const clearInput = useCallback7(() => {
5218
+ const clearInput = useCallback11(() => {
4049
5219
  setInput("");
4050
5220
  }, []);
4051
- const showModal = useCallback7((type, content) => {
5221
+ const showModal = useCallback11((type, content) => {
4052
5222
  setModal({ type, content, scrollOffset: 0 });
4053
5223
  }, []);
4054
- const closeModal = useCallback7(() => {
5224
+ const closeModal = useCallback11(() => {
4055
5225
  setModal({ type: null, content: "", scrollOffset: 0 });
4056
5226
  }, []);
4057
- const handleModalScroll = useCallback7((delta) => {
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 = useCallback7(() => {
5236
+ const handleExit = useCallback11(() => {
4067
5237
  cancelAllInputRequests();
4068
5238
  cleanupAllProcesses().catch(() => {
4069
5239
  });
4070
5240
  exit();
4071
- setTimeout(() => process.exit(0), DISPLAY_LIMITS.EXIT_DELAY);
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 = useCallback7(() => {
5264
+ const handleRecallQueuedInput = useCallback11(() => {
4088
5265
  return recallQueuedInput();
4089
5266
  }, [recallQueuedInput]);
4090
- const handleSecretSubmit = useCallback7((value) => {
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 = useCallback7(async (value) => {
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 useState4, useEffect as useEffect5 } from "react";
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] = useState4({
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 useState5, useEffect as useEffect6 } from "react";
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] = useState5(0);
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 useState6, useEffect as useEffect7, useRef as useRef6 } from "react";
5960
+ import { useState as useState10, useEffect as useEffect7, useRef as useRef9 } from "react";
4784
5961
  var useStatusTimer = (currentStatus, isProcessing) => {
4785
- const [statusElapsed, setStatusElapsed] = useState6(0);
4786
- const lastStatusRef = useRef6("");
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 useCallback8, useRef as useRef8, memo as memo10, useState as useState8 } from "react";
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 useState7, useEffect as useEffect8, useRef as useRef7, useMemo as useMemo3 } from "react";
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] = useState7(chars.length);
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 = useRef7("");
5121
- const lastInputTimeRef = useRef7(0);
5122
- const valueRef = useRef7(value || "");
6297
+ const lastInputRef = useRef10("");
6298
+ const lastInputTimeRef = useRef10(0);
6299
+ const valueRef = useRef10(value || "");
5123
6300
  valueRef.current = value || "";
5124
- const mouseFragmentRef = useRef7("");
5125
- const cursorRef = useRef7(cursor);
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] = useState8(0);
6493
+ const [selectedIdx, setSelectedIdx] = useState12(0);
5317
6494
  const clampedIdx = Math.min(selectedIdx, Math.max(0, suggestions.length - 1));
5318
- const selectedIdxRef = useRef8(clampedIdx);
6495
+ const selectedIdxRef = useRef11(clampedIdx);
5319
6496
  selectedIdxRef.current = clampedIdx;
5320
- const suggestionsRef = useRef8(suggestions);
6497
+ const suggestionsRef = useRef11(suggestions);
5321
6498
  suggestionsRef.current = suggestions;
5322
- const isSlashModeRef = useRef8(isSlashMode);
6499
+ const isSlashModeRef = useRef11(isSlashMode);
5323
6500
  isSlashModeRef.current = isSlashMode;
5324
- const hasArgsRef = useRef8(hasArgs);
6501
+ const hasArgsRef = useRef11(hasArgs);
5325
6502
  hasArgsRef.current = hasArgs;
5326
- const showPreviewRef = useRef8(showPreview);
6503
+ const showPreviewRef = useRef11(showPreview);
5327
6504
  showPreviewRef.current = showPreview;
5328
- const inputRequestRef = useRef8(inputRequest);
6505
+ const inputRequestRef = useRef11(inputRequest);
5329
6506
  inputRequestRef.current = inputRequest;
5330
- const onChangeRef = useRef8(onChange);
6507
+ const onChangeRef = useRef11(onChange);
5331
6508
  onChangeRef.current = onChange;
5332
- const onRecallQueuedInputRef = useRef8(onRecallQueuedInput);
6509
+ const onRecallQueuedInputRef = useRef11(onRecallQueuedInput);
5333
6510
  onRecallQueuedInputRef.current = onRecallQueuedInput;
5334
- const queuedCountRef = useRef8(queuedCount);
6511
+ const queuedCountRef = useRef11(queuedCount);
5335
6512
  queuedCountRef.current = queuedCount;
5336
- const latestValueRef = useRef8(value);
6513
+ const latestValueRef = useRef11(value);
5337
6514
  latestValueRef.current = value;
5338
- const handleLocalChange = useCallback8((newVal) => {
6515
+ const handleLocalChange = useCallback12((newVal) => {
5339
6516
  latestValueRef.current = newVal;
5340
6517
  onChange(newVal);
5341
6518
  }, [onChange]);
5342
- const latestSecretRef = useRef8(secretInput);
6519
+ const latestSecretRef = useRef11(secretInput);
5343
6520
  latestSecretRef.current = secretInput;
5344
- const handleSecretChange = useCallback8((newVal) => {
6521
+ const handleSecretChange = useCallback12((newVal) => {
5345
6522
  latestSecretRef.current = newVal;
5346
6523
  setSecretInput(newVal);
5347
6524
  }, [setSecretInput]);
5348
- const [inputKey, setInputKey] = useState8(0);
5349
- const completeCommand = useCallback8((idx) => {
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 = useRef8(onSubmit);
6536
+ const onSubmitRef = useRef11(onSubmit);
5360
6537
  onSubmitRef.current = onSubmit;
5361
- const wrappedOnSubmit = useCallback8((_staleVal) => {
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 = useRef8(onSecretSubmit);
6558
+ const onSecretSubmitRef = useRef11(onSecretSubmit);
5382
6559
  onSecretSubmitRef.current = onSecretSubmit;
5383
- const wrappedSecretSubmit = useCallback8((_staleVal) => {
6560
+ const wrappedSecretSubmit = useCallback12((_staleVal) => {
5384
6561
  onSecretSubmitRef.current(latestSecretRef.current);
5385
6562
  }, []);
5386
- const handleSpecialKey = useCallback8((key) => {
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 useState9, useEffect as useEffect9 } from "react";
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] = useState9(false);
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 useCallback9, useRef as useRef9 } from "react";
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 = useRef9(onScroll);
6728
+ const onScrollRef = useRef12(onScroll);
5552
6729
  onScrollRef.current = onScroll;
5553
- const onCloseRef = useRef9(onClose);
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(useCallback9((input, key) => {
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 useState10 } from "react";
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] = useState10(0);
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));