@pensar/apex 0.0.57 → 0.0.58-canary.b0e3654f

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.
Files changed (2) hide show
  1. package/build/index.js +1145 -529
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -69284,7 +69284,7 @@ function createRoot(renderer) {
69284
69284
  }
69285
69285
 
69286
69286
  // src/tui/index.tsx
69287
- var import_react87 = __toESM(require_react(), 1);
69287
+ var import_react88 = __toESM(require_react(), 1);
69288
69288
 
69289
69289
  // src/tui/components/footer.tsx
69290
69290
  import os5 from "os";
@@ -70114,13 +70114,13 @@ async function get() {
70114
70114
  return {
70115
70115
  ...parsedConfig,
70116
70116
  version,
70117
- openAiAPIKey: process.env.OPENAI_API_KEY,
70118
- anthropicAPIKey: process.env.ANTHROPIC_API_KEY,
70119
- openRouterAPIKey: process.env.OPENROUTER_API_KEY,
70120
- bedrockAPIKey: process.env.BEDROCK_API_KEY,
70121
- daytonaAPIKey: process.env.DAYTONA_API_KEY,
70122
- daytonaOrgId: process.env.DAYTONA_ORG_ID,
70123
- runloopAPIKey: process.env.RUNLOOP_API_KEY
70117
+ openAiAPIKey: process.env.OPENAI_API_KEY ?? parsedConfig.openAiAPIKey,
70118
+ anthropicAPIKey: process.env.ANTHROPIC_API_KEY ?? parsedConfig.anthropicAPIKey,
70119
+ openRouterAPIKey: process.env.OPENROUTER_API_KEY ?? parsedConfig.openRouterAPIKey,
70120
+ bedrockAPIKey: process.env.BEDROCK_API_KEY ?? parsedConfig.bedrockAPIKey,
70121
+ daytonaAPIKey: process.env.DAYTONA_API_KEY ?? parsedConfig.daytonaAPIKey,
70122
+ daytonaOrgId: process.env.DAYTONA_ORG_ID ?? parsedConfig.daytonaOrgId,
70123
+ runloopAPIKey: process.env.RUNLOOP_API_KEY ?? parsedConfig.runloopAPIKey
70124
70124
  };
70125
70125
  }
70126
70126
  async function update(config) {
@@ -71905,14 +71905,14 @@ var commands = [
71905
71905
  }
71906
71906
  },
71907
71907
  {
71908
- name: "resume",
71909
- aliases: ["r"],
71910
- description: "Resume a previous pentest session",
71908
+ name: "sessions",
71909
+ aliases: ["s"],
71910
+ description: "Browse previous sessions",
71911
71911
  category: "Pentesting",
71912
71912
  handler: async (args, ctx3) => {
71913
71913
  ctx3.navigate({
71914
71914
  type: "base",
71915
- path: "resume"
71915
+ path: "sessions"
71916
71916
  });
71917
71917
  }
71918
71918
  },
@@ -71921,6 +71921,7 @@ var commands = [
71921
71921
  aliases: ["c"],
71922
71922
  description: "Open the Chat TUI interface",
71923
71923
  category: "General",
71924
+ hidden: true,
71924
71925
  handler: async (args, ctx3) => {
71925
71926
  ctx3.navigate({
71926
71927
  type: "base",
@@ -71933,6 +71934,7 @@ var commands = [
71933
71934
  aliases: ["t"],
71934
71935
  description: "View and manage active tools (session only)",
71935
71936
  category: "Session",
71937
+ hidden: true,
71936
71938
  handler: async (args, ctx3) => {
71937
71939
  if (ctx3.route.type !== "session") {
71938
71940
  return;
@@ -71979,6 +71981,8 @@ function CommandProvider({ children }) {
71979
71981
  const options = [];
71980
71982
  for (const cmd of routerCommands) {
71981
71983
  const cmdConfig = commands.find((c) => c.name === cmd.name);
71984
+ if (cmdConfig?.hidden)
71985
+ continue;
71982
71986
  let description = cmd.description || "";
71983
71987
  options.push({
71984
71988
  value: `/${cmd.name}`,
@@ -74216,7 +74220,7 @@ var PromptInput = import_react25.forwardRef(function PromptInput2({
74216
74220
  onSubmit,
74217
74221
  enableAutocomplete = false,
74218
74222
  autocompleteOptions = [],
74219
- maxSuggestions = 6,
74223
+ maxSuggestions = 10,
74220
74224
  enableCommands = false,
74221
74225
  onCommandExecute,
74222
74226
  showPromptIndicator = false
@@ -74502,6 +74506,13 @@ var SubAgentDisplay = import_react27.memo(function SubAgentDisplay2({
74502
74506
  subagent.name
74503
74507
  ]
74504
74508
  }, undefined, true, undefined, this),
74509
+ subagent.status === "paused" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
74510
+ fg: colors.orangeText,
74511
+ children: [
74512
+ "⏸ ",
74513
+ subagent.name
74514
+ ]
74515
+ }, undefined, true, undefined, this),
74505
74516
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
74506
74517
  fg: "gray",
74507
74518
  children: open ? "▼" : "▶"
@@ -74837,15 +74848,31 @@ function SwarmDashboard({
74837
74848
  isCompleted,
74838
74849
  onBack,
74839
74850
  onViewReport,
74840
- onKillAgent
74851
+ onKillAgent,
74852
+ onResumeAgent
74841
74853
  }) {
74842
74854
  const [currentView, setCurrentView] = import_react31.useState("overview");
74843
74855
  const [selectedAgentId, setSelectedAgentId] = import_react31.useState(null);
74844
74856
  const [focusedIndex, setFocusedIndex] = import_react31.useState(0);
74845
74857
  const [showDiscoveryLogs, setShowDiscoveryLogs] = import_react31.useState(false);
74846
74858
  const { stack, externalDialogOpen } = useDialog();
74847
- const discoveryAgent = import_react31.useMemo(() => subagents.find((s) => s.type === "attack-surface") || null, [subagents]);
74859
+ const discoveryAgent = import_react31.useMemo(() => subagents.find((s) => s.type === "attack-surface" && s.status === "pending") || subagents.find((s) => s.type === "attack-surface") || null, [subagents]);
74848
74860
  const pentestAgents = import_react31.useMemo(() => subagents.filter((s) => s.type === "pentest"), [subagents]);
74861
+ const allEndpoints = import_react31.useMemo(() => {
74862
+ const endpointSet = new Set;
74863
+ const attackSurfaceAgents = subagents.filter((s) => s.type === "attack-surface");
74864
+ for (const agent of attackSurfaceAgents) {
74865
+ for (const msg of agent.messages) {
74866
+ if (msg.role === "assistant" && typeof msg.content === "string") {
74867
+ const urlMatches = msg.content.match(/\/[a-zA-Z0-9\/_.-]+/g);
74868
+ if (urlMatches) {
74869
+ urlMatches.forEach((u) => endpointSet.add(u));
74870
+ }
74871
+ }
74872
+ }
74873
+ }
74874
+ return Array.from(endpointSet).slice(0, 50);
74875
+ }, [subagents]);
74849
74876
  const metrics = import_react31.useMemo(() => {
74850
74877
  const findingsCount = subagents.reduce((sum, s) => {
74851
74878
  return sum + s.messages.filter((m2) => m2.role === "assistant" && typeof m2.content === "string" && (m2.content.includes("finding") || m2.content.includes("vulnerability"))).length;
@@ -74868,6 +74895,10 @@ function SwarmDashboard({
74868
74895
  setShowDiscoveryLogs((prev) => !prev);
74869
74896
  return;
74870
74897
  }
74898
+ if ((key.name === "r" || key.name === "R") && discoveryAgent?.status === "paused") {
74899
+ onResumeAgent?.(discoveryAgent.id);
74900
+ return;
74901
+ }
74871
74902
  const agentCount = pentestAgents.length;
74872
74903
  if (key.name === "escape") {
74873
74904
  if (showDiscoveryLogs) {
@@ -74922,6 +74953,10 @@ function SwarmDashboard({
74922
74953
  setSelectedAgentId(null);
74923
74954
  return;
74924
74955
  }
74956
+ if ((key.name === "r" || key.name === "R") && selectedAgent?.status === "paused") {
74957
+ onResumeAgent?.(selectedAgent.id);
74958
+ return;
74959
+ }
74925
74960
  }
74926
74961
  });
74927
74962
  if (currentView === "detail" && selectedAgent) {
@@ -74930,7 +74965,8 @@ function SwarmDashboard({
74930
74965
  onBack: () => {
74931
74966
  setCurrentView("overview");
74932
74967
  setSelectedAgentId(null);
74933
- }
74968
+ },
74969
+ onResume: selectedAgent.status === "paused" ? () => onResumeAgent?.(selectedAgent.id) : undefined
74934
74970
  }, undefined, false, undefined, this);
74935
74971
  }
74936
74972
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
@@ -74947,6 +74983,7 @@ function SwarmDashboard({
74947
74983
  children: [
74948
74984
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(DiscoveryPanel, {
74949
74985
  agent: discoveryAgent,
74986
+ endpoints: allEndpoints,
74950
74987
  showLogs: showDiscoveryLogs,
74951
74988
  onToggleLogs: () => setShowDiscoveryLogs((prev) => !prev)
74952
74989
  }, undefined, false, undefined, this),
@@ -74962,7 +74999,28 @@ function SwarmDashboard({
74962
74999
  borderColor: dimText,
74963
75000
  backgroundColor: darkBg,
74964
75001
  padding: 2,
74965
- children: discoveryAgent?.status === "pending" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75002
+ children: discoveryAgent?.status === "paused" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75003
+ flexDirection: "column",
75004
+ alignItems: "center",
75005
+ gap: 1,
75006
+ children: [
75007
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75008
+ fg: colors.orangeText,
75009
+ children: "⏸ Discovery was interrupted"
75010
+ }, undefined, false, undefined, this),
75011
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75012
+ fg: dimText,
75013
+ children: [
75014
+ "Press ",
75015
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75016
+ fg: colors.orangeText,
75017
+ children: "[R]"
75018
+ }, undefined, false, undefined, this),
75019
+ " to resume"
75020
+ ]
75021
+ }, undefined, true, undefined, this)
75022
+ ]
75023
+ }, undefined, true, undefined, this) : discoveryAgent?.status === "pending" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
74966
75024
  flexDirection: "column",
74967
75025
  alignItems: "center",
74968
75026
  gap: 1,
@@ -75051,30 +75109,18 @@ function SwarmDashboard({
75051
75109
  canceledAgents: metrics.canceledAgents,
75052
75110
  duration: metrics.duration,
75053
75111
  isExecuting,
75054
- showKillHint: !!onKillAgent
75112
+ showKillHint: !!onKillAgent,
75113
+ discoveryPaused: discoveryAgent?.status === "paused"
75055
75114
  }, undefined, false, undefined, this)
75056
75115
  ]
75057
75116
  }, undefined, true, undefined, this);
75058
75117
  }
75059
75118
  function DiscoveryPanel({
75060
75119
  agent,
75120
+ endpoints,
75061
75121
  showLogs,
75062
75122
  onToggleLogs
75063
75123
  }) {
75064
- const endpoints = import_react31.useMemo(() => {
75065
- if (!agent)
75066
- return [];
75067
- const endpointSet = new Set;
75068
- for (const msg of agent.messages) {
75069
- if (msg.role === "assistant" && typeof msg.content === "string") {
75070
- const urlMatches = msg.content.match(/\/[a-zA-Z0-9\/_-]+/g);
75071
- if (urlMatches) {
75072
- urlMatches.forEach((u) => endpointSet.add(u));
75073
- }
75074
- }
75075
- }
75076
- return Array.from(endpointSet).slice(0, 50);
75077
- }, [agent?.messages]);
75078
75124
  if (showLogs && agent) {
75079
75125
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75080
75126
  flexGrow: 1,
@@ -75106,6 +75152,10 @@ function DiscoveryPanel({
75106
75152
  agent.status === "failed" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75107
75153
  fg: "red",
75108
75154
  children: "✗ Attack Surface Discovery"
75155
+ }, undefined, false, undefined, this),
75156
+ agent.status === "paused" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75157
+ fg: colors.orangeText,
75158
+ children: "⏸ Attack Surface Discovery (Paused)"
75109
75159
  }, undefined, false, undefined, this)
75110
75160
  ]
75111
75161
  }, undefined, true, undefined, this),
@@ -75196,6 +75246,10 @@ function DiscoveryPanel({
75196
75246
  agent?.status === "failed" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75197
75247
  fg: "red",
75198
75248
  children: "✗ Discovery"
75249
+ }, undefined, false, undefined, this),
75250
+ agent?.status === "paused" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75251
+ fg: colors.orangeText,
75252
+ children: "⏸ Discovery"
75199
75253
  }, undefined, false, undefined, this)
75200
75254
  ]
75201
75255
  }, undefined, true, undefined, this),
@@ -75217,7 +75271,7 @@ function DiscoveryPanel({
75217
75271
  children: "Status: "
75218
75272
  }, undefined, false, undefined, this),
75219
75273
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75220
- fg: agent.status === "pending" ? greenBullet : creamText,
75274
+ fg: agent.status === "pending" ? greenBullet : agent.status === "paused" ? colors.orangeText : creamText,
75221
75275
  children: agent.status
75222
75276
  }, undefined, false, undefined, this)
75223
75277
  ]
@@ -75245,6 +75299,18 @@ function DiscoveryPanel({
75245
75299
  children: endpoints.length
75246
75300
  }, undefined, false, undefined, this)
75247
75301
  ]
75302
+ }, undefined, true, undefined, this),
75303
+ agent.status === "paused" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75304
+ children: [
75305
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75306
+ fg: colors.orangeText,
75307
+ children: "[R]"
75308
+ }, undefined, false, undefined, this),
75309
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75310
+ fg: dimText,
75311
+ children: " Resume"
75312
+ }, undefined, false, undefined, this)
75313
+ ]
75248
75314
  }, undefined, true, undefined, this)
75249
75315
  ]
75250
75316
  }, undefined, true, undefined, this),
@@ -75280,12 +75346,14 @@ function AgentCard({ agent, focused, onSelect }) {
75280
75346
  pending: "◐",
75281
75347
  completed: "✓",
75282
75348
  failed: "✗",
75349
+ paused: "⏸",
75283
75350
  canceled: "⊘"
75284
75351
  }[agent.status];
75285
75352
  const statusColor = {
75286
75353
  pending: greenBullet,
75287
75354
  completed: greenBullet,
75288
75355
  failed: RGBA.fromInts(244, 67, 54, 255),
75356
+ paused: colors.orangeText,
75289
75357
  canceled: dimText
75290
75358
  }[agent.status];
75291
75359
  const lastActivity = import_react31.useMemo(() => {
@@ -75377,6 +75445,9 @@ function AgentCard({ agent, focused, onSelect }) {
75377
75445
  }, undefined, false, undefined, this) : agent.status === "pending" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SpinnerDots, {
75378
75446
  label: lastActivity,
75379
75447
  fg: "green"
75448
+ }, undefined, false, undefined, this) : agent.status === "paused" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75449
+ fg: colors.orangeText,
75450
+ children: "⏸ Paused — press Enter then R to resume"
75380
75451
  }, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75381
75452
  fg: agent.status === "completed" ? greenBullet : dimText,
75382
75453
  children: agent.status === "completed" ? "✓ Complete" : lastActivity
@@ -75433,7 +75504,8 @@ function MetricsBar({
75433
75504
  canceledAgents,
75434
75505
  duration,
75435
75506
  isExecuting,
75436
- showKillHint
75507
+ showKillHint,
75508
+ discoveryPaused
75437
75509
  }) {
75438
75510
  const formattedDuration = import_react31.useMemo(() => {
75439
75511
  const mins = Math.floor(duration / 60);
@@ -75518,6 +75590,18 @@ function MetricsBar({
75518
75590
  flexDirection: "row",
75519
75591
  gap: 2,
75520
75592
  children: [
75593
+ discoveryPaused && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75594
+ children: [
75595
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75596
+ fg: colors.orangeText,
75597
+ children: "[R]"
75598
+ }, undefined, false, undefined, this),
75599
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75600
+ fg: dimText,
75601
+ children: " Resume"
75602
+ }, undefined, false, undefined, this)
75603
+ ]
75604
+ }, undefined, true, undefined, this),
75521
75605
  showKillHint && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75522
75606
  children: [
75523
75607
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
@@ -75583,11 +75667,12 @@ function MetricsBar({
75583
75667
  ]
75584
75668
  }, undefined, true, undefined, this);
75585
75669
  }
75586
- function AgentDetailView({ agent, onBack }) {
75670
+ function AgentDetailView({ agent, onBack, onResume }) {
75587
75671
  const statusColor = {
75588
75672
  pending: greenBullet,
75589
75673
  completed: greenBullet,
75590
75674
  failed: RGBA.fromInts(244, 67, 54, 255),
75675
+ paused: colors.orangeText,
75591
75676
  canceled: dimText
75592
75677
  }[agent.status];
75593
75678
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
@@ -75670,6 +75755,18 @@ function AgentDetailView({ agent, onBack }) {
75670
75755
  flexDirection: "row",
75671
75756
  gap: 2,
75672
75757
  children: [
75758
+ agent.status === "paused" && onResume && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75759
+ children: [
75760
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75761
+ fg: colors.orangeText,
75762
+ children: "[R]"
75763
+ }, undefined, false, undefined, this),
75764
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75765
+ fg: dimText,
75766
+ children: " Resume"
75767
+ }, undefined, false, undefined, this)
75768
+ ]
75769
+ }, undefined, true, undefined, this),
75673
75770
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75674
75771
  children: [
75675
75772
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
@@ -145902,6 +145999,319 @@ function ToolsPanel({
145902
145999
  }, undefined, false, undefined, this);
145903
146000
  }
145904
146001
 
146002
+ // src/core/session/loader.ts
146003
+ import { join as join15 } from "path";
146004
+ import { existsSync as existsSync20, readdirSync as readdirSync5, readFileSync as readFileSync11 } from "fs";
146005
+ function convertMessagesToUI(messages, baseTime) {
146006
+ const uiMessages = [];
146007
+ let messageIndex = 0;
146008
+ const toolResults = new Map;
146009
+ for (const msg of messages) {
146010
+ if (Array.isArray(msg.content)) {
146011
+ for (const part of msg.content) {
146012
+ if (part.type === "tool-result" && part.toolCallId) {
146013
+ toolResults.set(part.toolCallId, part.output);
146014
+ }
146015
+ }
146016
+ }
146017
+ }
146018
+ for (const msg of messages) {
146019
+ const createdAt = new Date(baseTime.getTime() + messageIndex * 1000);
146020
+ messageIndex++;
146021
+ if (typeof msg.content === "string") {
146022
+ uiMessages.push({
146023
+ role: msg.role,
146024
+ content: msg.content,
146025
+ createdAt
146026
+ });
146027
+ } else if (Array.isArray(msg.content)) {
146028
+ for (const part of msg.content) {
146029
+ if (part.type === "text" && part.text) {
146030
+ uiMessages.push({
146031
+ role: "assistant",
146032
+ content: part.text,
146033
+ createdAt
146034
+ });
146035
+ } else if (part.type === "tool-call") {
146036
+ const toolDescription = typeof part.input?.toolCallDescription === "string" ? part.input.toolCallDescription : part.toolName || "tool";
146037
+ const result = part.toolCallId ? toolResults.get(part.toolCallId) : undefined;
146038
+ uiMessages.push({
146039
+ role: "tool",
146040
+ content: toolDescription,
146041
+ createdAt,
146042
+ toolCallId: part.toolCallId,
146043
+ toolName: part.toolName,
146044
+ args: part.input,
146045
+ result,
146046
+ status: "completed"
146047
+ });
146048
+ }
146049
+ }
146050
+ }
146051
+ }
146052
+ return uiMessages;
146053
+ }
146054
+ function parseSubagentFilename(filename) {
146055
+ if (filename.startsWith("attack-surface-agent")) {
146056
+ return { agentType: "attack-surface", name: "Attack Surface Discovery" };
146057
+ }
146058
+ if (filename.startsWith("vuln-test-")) {
146059
+ const parts = filename.replace("vuln-test-", "").split("-");
146060
+ const vulnClass = parts[0] || "generic";
146061
+ const vulnClassFormatted = vulnClass.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
146062
+ return {
146063
+ agentType: "pentest",
146064
+ name: `${vulnClassFormatted} Test`
146065
+ };
146066
+ }
146067
+ if (filename.startsWith("orchestrator-")) {
146068
+ return { agentType: "pentest", name: "Orchestrator Summary" };
146069
+ }
146070
+ return { agentType: "pentest", name: filename.split("-")[0] || "Unknown" };
146071
+ }
146072
+ function loadSubagents(rootPath) {
146073
+ const subagentsPath = join15(rootPath, "subagents");
146074
+ if (!existsSync20(subagentsPath)) {
146075
+ return [];
146076
+ }
146077
+ const subagents = [];
146078
+ const files = readdirSync5(subagentsPath).filter((f) => f.endsWith(".json"));
146079
+ files.sort((a, b2) => {
146080
+ const timeA = a.match(/\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}/)?.[0] || "";
146081
+ const timeB = b2.match(/\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}/)?.[0] || "";
146082
+ return timeA.localeCompare(timeB);
146083
+ });
146084
+ for (const file2 of files) {
146085
+ try {
146086
+ const filePath = join15(subagentsPath, file2);
146087
+ const data = JSON.parse(readFileSync11(filePath, "utf-8"));
146088
+ const { agentType, name: name39 } = parseSubagentFilename(file2);
146089
+ const timestamp = new Date(data.timestamp);
146090
+ if (file2.startsWith("orchestrator-")) {
146091
+ continue;
146092
+ }
146093
+ const messages = convertMessagesToUI(data.messages, timestamp);
146094
+ let status = "completed";
146095
+ switch (data.status) {
146096
+ case "canceled":
146097
+ case "failed":
146098
+ case "completed":
146099
+ case "pending":
146100
+ status = data.status;
146101
+ break;
146102
+ default:
146103
+ if (typeof data.findingsCount === "number" && data.findingsCount < 0) {
146104
+ status = "failed";
146105
+ }
146106
+ break;
146107
+ }
146108
+ subagents.push({
146109
+ id: `loaded-${file2.replace(".json", "")}`,
146110
+ name: data.agentName === "attack-surface-agent" ? "Attack Surface Discovery" : name39,
146111
+ type: agentType,
146112
+ target: data.target || "Unknown",
146113
+ messages,
146114
+ createdAt: timestamp,
146115
+ status
146116
+ });
146117
+ } catch (e) {
146118
+ console.error(`Failed to load subagent file ${file2}:`, e);
146119
+ }
146120
+ }
146121
+ return subagents;
146122
+ }
146123
+ function loadAttackSurfaceResults2(rootPath) {
146124
+ const resultsPath = join15(rootPath, "attack-surface-results.json");
146125
+ if (!existsSync20(resultsPath)) {
146126
+ return null;
146127
+ }
146128
+ try {
146129
+ return JSON.parse(readFileSync11(resultsPath, "utf-8"));
146130
+ } catch (e) {
146131
+ console.error("Failed to load attack surface results:", e);
146132
+ return null;
146133
+ }
146134
+ }
146135
+ function hasReport(rootPath) {
146136
+ const reportPath = join15(rootPath, "comprehensive-pentest-report.md");
146137
+ return existsSync20(reportPath);
146138
+ }
146139
+ function createDiscoveryFromLogs(rootPath, session) {
146140
+ const logPath = join15(rootPath, "logs", "streamlined-pentest.log");
146141
+ if (!existsSync20(logPath)) {
146142
+ return null;
146143
+ }
146144
+ try {
146145
+ const logContent = readFileSync11(logPath, "utf-8");
146146
+ const lines = logContent.split(`
146147
+ `).filter(Boolean);
146148
+ const messages = [];
146149
+ let stepBuffer = "";
146150
+ for (const line of lines) {
146151
+ const match = line.match(/^(\d{4}-\d{2}-\d{2}T[\d:.]+Z) - \[(\w+)\] (.+)$/);
146152
+ if (!match)
146153
+ continue;
146154
+ const [, timestamp, level, content] = match;
146155
+ const createdAt = new Date(timestamp);
146156
+ if (content.startsWith("[Tool]")) {
146157
+ const toolMatch = content.match(/\[Tool\] (\w+): (.+)/);
146158
+ if (toolMatch) {
146159
+ messages.push({
146160
+ role: "tool",
146161
+ content: `✓ ${toolMatch[2]}`,
146162
+ createdAt,
146163
+ toolName: toolMatch[1],
146164
+ status: "completed"
146165
+ });
146166
+ }
146167
+ } else if (content.startsWith("[Step")) {
146168
+ const stepMatch = content.match(/\[Step \d+\] (.+)/);
146169
+ if (stepMatch) {
146170
+ messages.push({
146171
+ role: "assistant",
146172
+ content: stepMatch[1],
146173
+ createdAt
146174
+ });
146175
+ }
146176
+ }
146177
+ }
146178
+ if (messages.length === 0) {
146179
+ return null;
146180
+ }
146181
+ return {
146182
+ id: "discovery-from-logs",
146183
+ name: "Attack Surface Discovery",
146184
+ type: "attack-surface",
146185
+ target: session.targets[0] || "Unknown",
146186
+ messages,
146187
+ createdAt: new Date(session.time.created),
146188
+ status: "completed"
146189
+ };
146190
+ } catch (e) {
146191
+ console.error("Failed to parse logs:", e);
146192
+ return null;
146193
+ }
146194
+ }
146195
+ async function loadSessionState(session) {
146196
+ const rootPath = session.rootPath;
146197
+ let subagents = loadSubagents(rootPath);
146198
+ const hasAttackSurfaceAgent = subagents.some((s) => s.type === "attack-surface");
146199
+ if (!hasAttackSurfaceAgent) {
146200
+ const discoveryAgent = createDiscoveryFromLogs(rootPath, session);
146201
+ if (discoveryAgent) {
146202
+ subagents = [discoveryAgent, ...subagents];
146203
+ }
146204
+ }
146205
+ const manifestPath = join15(rootPath, "agent-manifest.json");
146206
+ if (existsSync20(manifestPath)) {
146207
+ try {
146208
+ const manifest = JSON.parse(readFileSync11(manifestPath, "utf-8"));
146209
+ for (const entry of manifest) {
146210
+ if (entry.status !== "running")
146211
+ continue;
146212
+ const resumeInfo = {
146213
+ target: entry.target,
146214
+ objective: entry.objective,
146215
+ vulnerabilityClass: entry.vulnerabilityClass,
146216
+ authenticationInfo: entry.authInfo
146217
+ };
146218
+ const vulnSlug = entry.vulnerabilityClass.toLowerCase().replace(/\s+/g, "-");
146219
+ const existingIndex = subagents.findIndex((s) => s.type === "pentest" && s.id.includes(`-${vulnSlug}-`) && s.target === entry.target);
146220
+ if (existingIndex >= 0) {
146221
+ subagents[existingIndex] = {
146222
+ ...subagents[existingIndex],
146223
+ status: "paused",
146224
+ resumeInfo
146225
+ };
146226
+ } else {
146227
+ let messages = [];
146228
+ const subagentsPath = join15(rootPath, "subagents");
146229
+ if (existsSync20(subagentsPath)) {
146230
+ try {
146231
+ const files = readdirSync5(subagentsPath).filter((f) => f.endsWith(".json"));
146232
+ const matchingFile = files.find((f) => {
146233
+ if (vulnSlug && f.includes(vulnSlug))
146234
+ return true;
146235
+ return false;
146236
+ });
146237
+ if (matchingFile) {
146238
+ const data = JSON.parse(readFileSync11(join15(subagentsPath, matchingFile), "utf-8"));
146239
+ messages = convertMessagesToUI(data.messages, new Date(entry.spawnedAt));
146240
+ }
146241
+ } catch {}
146242
+ }
146243
+ subagents.push({
146244
+ id: entry.id,
146245
+ name: entry.name,
146246
+ type: "pentest",
146247
+ target: entry.target,
146248
+ messages,
146249
+ createdAt: new Date(entry.spawnedAt),
146250
+ status: "paused",
146251
+ resumeInfo
146252
+ });
146253
+ }
146254
+ }
146255
+ } catch (e) {
146256
+ console.error("Failed to load agent manifest:", e);
146257
+ }
146258
+ }
146259
+ const attackSurfaceResults = loadAttackSurfaceResults2(rootPath);
146260
+ const hasReportFile = hasReport(rootPath);
146261
+ const hasDiscoverySubagent = subagents.some((s) => s.type === "attack-surface");
146262
+ const interruptedDuringDiscovery = !attackSurfaceResults && !hasReportFile && hasDiscoverySubagent;
146263
+ if (interruptedDuringDiscovery) {
146264
+ for (let i = 0;i < subagents.length; i++) {
146265
+ if (subagents[i].type === "attack-surface" && subagents[i].status === "completed") {
146266
+ subagents[i] = { ...subagents[i], status: "paused" };
146267
+ }
146268
+ }
146269
+ }
146270
+ const isComplete = hasReportFile || attackSurfaceResults?.summary?.analysisComplete === true && subagents.length > 1;
146271
+ return {
146272
+ session,
146273
+ subagents,
146274
+ attackSurfaceResults,
146275
+ isComplete,
146276
+ hasReport: hasReportFile,
146277
+ interruptedDuringDiscovery
146278
+ };
146279
+ }
146280
+
146281
+ // src/tui/session/swarm-to-operator-adapter.ts
146282
+ function adaptSwarmStateForOperator(state) {
146283
+ const messages = [];
146284
+ messages.push({
146285
+ role: "system",
146286
+ content: `Resuming from auto-mode session. ${state.subagents.length} subagent(s) ran. ${state.hasReport ? "Report available." : ""}`,
146287
+ createdAt: new Date
146288
+ });
146289
+ for (const sub of state.subagents) {
146290
+ const assistantMsgs = sub.messages.filter((m2) => m2.role === "assistant" && typeof m2.content === "string" && m2.content.trim());
146291
+ const lastSummary = assistantMsgs[assistantMsgs.length - 1];
146292
+ if (lastSummary) {
146293
+ messages.push({
146294
+ role: "assistant",
146295
+ content: `[${sub.name}] ${lastSummary.content}`,
146296
+ createdAt: lastSummary.createdAt
146297
+ });
146298
+ }
146299
+ }
146300
+ const attackSurface = [];
146301
+ if (state.attackSurfaceResults?.targets) {
146302
+ for (const target of state.attackSurfaceResults.targets) {
146303
+ attackSurface.push({
146304
+ id: `synth-${attackSurface.length}`,
146305
+ method: "GET",
146306
+ path: target.target,
146307
+ status: "discovered",
146308
+ category: target.objective || "unknown"
146309
+ });
146310
+ }
146311
+ }
146312
+ return { messages, attackSurface };
146313
+ }
146314
+
145905
146315
  // src/tui/components/chat/header.tsx
145906
146316
  function Header({
145907
146317
  mode,
@@ -147220,6 +147630,7 @@ function SessionComponent({
147220
147630
  mode,
147221
147631
  model: propModel,
147222
147632
  isResume = false,
147633
+ openAsOperator = false,
147223
147634
  initialDirective,
147224
147635
  onExit
147225
147636
  }) {
@@ -147301,7 +147712,7 @@ function SessionComponent({
147301
147712
  import_react46.useEffect(() => {
147302
147713
  if (!isResume || resumeLoaded)
147303
147714
  return;
147304
- Session.loadOperatorState(session.id).then((savedState) => {
147715
+ Session.loadOperatorState(session.id).then(async (savedState) => {
147305
147716
  if (savedState) {
147306
147717
  setOperatorMode(savedState.mode);
147307
147718
  setAutoApproveTier(savedState.autoApproveTier);
@@ -147322,10 +147733,15 @@ function SessionComponent({
147322
147733
  const history = savedState.actionHistory || [];
147323
147734
  setApprovedCount(history.filter((a) => a.decision === "approved" || a.decision === "auto-approved").length);
147324
147735
  setDeniedCount(history.filter((a) => a.decision === "denied").length);
147736
+ } else if (openAsOperator) {
147737
+ const swarmState = await loadSessionState(session);
147738
+ const { messages: synthMessages, attackSurface } = adaptSwarmStateForOperator(swarmState);
147739
+ setMessages(synthMessages);
147740
+ sidebar.updateState({ attackSurface });
147325
147741
  }
147326
147742
  setResumeLoaded(true);
147327
147743
  });
147328
- }, [isResume, session.id, resumeLoaded, setMessages, sidebar]);
147744
+ }, [isResume, session.id, resumeLoaded, setMessages, sidebar, openAsOperator]);
147329
147745
  import_react46.useEffect(() => {
147330
147746
  if (agent2)
147331
147747
  return;
@@ -147800,228 +148216,14 @@ Usage: /tier <1-5>`,
147800
148216
  }
147801
148217
 
147802
148218
  // src/tui/components/operator-dashboard/index.tsx
147803
- function OperatorDashboard({ session, isResume = false }) {
148219
+ function OperatorDashboard({ session, isResume = false, openAsOperator }) {
147804
148220
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SessionComponent, {
147805
148221
  session,
147806
148222
  mode: "operator",
147807
- isResume
148223
+ isResume,
148224
+ openAsOperator
147808
148225
  }, undefined, false, undefined, this);
147809
148226
  }
147810
-
147811
- // src/core/session/loader.ts
147812
- import { join as join15 } from "path";
147813
- import { existsSync as existsSync20, readdirSync as readdirSync5, readFileSync as readFileSync11 } from "fs";
147814
- function convertMessagesToUI(messages, baseTime) {
147815
- const uiMessages = [];
147816
- let messageIndex = 0;
147817
- const toolResults = new Map;
147818
- for (const msg of messages) {
147819
- if (Array.isArray(msg.content)) {
147820
- for (const part of msg.content) {
147821
- if (part.type === "tool-result" && part.toolCallId) {
147822
- toolResults.set(part.toolCallId, part.output);
147823
- }
147824
- }
147825
- }
147826
- }
147827
- for (const msg of messages) {
147828
- const createdAt = new Date(baseTime.getTime() + messageIndex * 1000);
147829
- messageIndex++;
147830
- if (typeof msg.content === "string") {
147831
- uiMessages.push({
147832
- role: msg.role,
147833
- content: msg.content,
147834
- createdAt
147835
- });
147836
- } else if (Array.isArray(msg.content)) {
147837
- for (const part of msg.content) {
147838
- if (part.type === "text" && part.text) {
147839
- uiMessages.push({
147840
- role: "assistant",
147841
- content: part.text,
147842
- createdAt
147843
- });
147844
- } else if (part.type === "tool-call") {
147845
- const toolDescription = typeof part.input?.toolCallDescription === "string" ? part.input.toolCallDescription : part.toolName || "tool";
147846
- const result = part.toolCallId ? toolResults.get(part.toolCallId) : undefined;
147847
- uiMessages.push({
147848
- role: "tool",
147849
- content: `✓ ${toolDescription}`,
147850
- createdAt,
147851
- toolCallId: part.toolCallId,
147852
- toolName: part.toolName,
147853
- args: part.input,
147854
- result,
147855
- status: "completed"
147856
- });
147857
- }
147858
- }
147859
- }
147860
- }
147861
- return uiMessages;
147862
- }
147863
- function parseSubagentFilename(filename) {
147864
- if (filename.startsWith("attack-surface-agent")) {
147865
- return { agentType: "attack-surface", name: "Attack Surface Discovery" };
147866
- }
147867
- if (filename.startsWith("vuln-test-")) {
147868
- const parts = filename.replace("vuln-test-", "").split("-");
147869
- const vulnClass = parts[0] || "generic";
147870
- const vulnClassFormatted = vulnClass.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
147871
- return {
147872
- agentType: "pentest",
147873
- name: `${vulnClassFormatted} Test`
147874
- };
147875
- }
147876
- if (filename.startsWith("orchestrator-")) {
147877
- return { agentType: "pentest", name: "Orchestrator Summary" };
147878
- }
147879
- return { agentType: "pentest", name: filename.split("-")[0] || "Unknown" };
147880
- }
147881
- function loadSubagents(rootPath) {
147882
- const subagentsPath = join15(rootPath, "subagents");
147883
- if (!existsSync20(subagentsPath)) {
147884
- return [];
147885
- }
147886
- const subagents = [];
147887
- const files = readdirSync5(subagentsPath).filter((f) => f.endsWith(".json"));
147888
- files.sort((a, b2) => {
147889
- const timeA = a.match(/\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}/)?.[0] || "";
147890
- const timeB = b2.match(/\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}/)?.[0] || "";
147891
- return timeA.localeCompare(timeB);
147892
- });
147893
- for (const file2 of files) {
147894
- try {
147895
- const filePath = join15(subagentsPath, file2);
147896
- const data = JSON.parse(readFileSync11(filePath, "utf-8"));
147897
- const { agentType, name: name39 } = parseSubagentFilename(file2);
147898
- const timestamp = new Date(data.timestamp);
147899
- if (file2.startsWith("orchestrator-")) {
147900
- continue;
147901
- }
147902
- const messages = convertMessagesToUI(data.messages, timestamp);
147903
- let status = "completed";
147904
- switch (data.status) {
147905
- case "canceled":
147906
- case "failed":
147907
- case "completed":
147908
- case "pending":
147909
- status = data.status;
147910
- break;
147911
- default:
147912
- if (typeof data.findingsCount === "number" && data.findingsCount < 0) {
147913
- status = "failed";
147914
- }
147915
- break;
147916
- }
147917
- subagents.push({
147918
- id: `loaded-${file2.replace(".json", "")}`,
147919
- name: data.agentName === "attack-surface-agent" ? "Attack Surface Discovery" : name39,
147920
- type: agentType,
147921
- target: data.target || "Unknown",
147922
- messages,
147923
- createdAt: timestamp,
147924
- status
147925
- });
147926
- } catch (e) {
147927
- console.error(`Failed to load subagent file ${file2}:`, e);
147928
- }
147929
- }
147930
- return subagents;
147931
- }
147932
- function loadAttackSurfaceResults2(rootPath) {
147933
- const resultsPath = join15(rootPath, "attack-surface-results.json");
147934
- if (!existsSync20(resultsPath)) {
147935
- return null;
147936
- }
147937
- try {
147938
- return JSON.parse(readFileSync11(resultsPath, "utf-8"));
147939
- } catch (e) {
147940
- console.error("Failed to load attack surface results:", e);
147941
- return null;
147942
- }
147943
- }
147944
- function hasReport(rootPath) {
147945
- const reportPath = join15(rootPath, "comprehensive-pentest-report.md");
147946
- return existsSync20(reportPath);
147947
- }
147948
- function createDiscoveryFromLogs(rootPath, session) {
147949
- const logPath = join15(rootPath, "logs", "streamlined-pentest.log");
147950
- if (!existsSync20(logPath)) {
147951
- return null;
147952
- }
147953
- try {
147954
- const logContent = readFileSync11(logPath, "utf-8");
147955
- const lines = logContent.split(`
147956
- `).filter(Boolean);
147957
- const messages = [];
147958
- let stepBuffer = "";
147959
- for (const line of lines) {
147960
- const match = line.match(/^(\d{4}-\d{2}-\d{2}T[\d:.]+Z) - \[(\w+)\] (.+)$/);
147961
- if (!match)
147962
- continue;
147963
- const [, timestamp, level, content] = match;
147964
- const createdAt = new Date(timestamp);
147965
- if (content.startsWith("[Tool]")) {
147966
- const toolMatch = content.match(/\[Tool\] (\w+): (.+)/);
147967
- if (toolMatch) {
147968
- messages.push({
147969
- role: "tool",
147970
- content: `✓ ${toolMatch[2]}`,
147971
- createdAt,
147972
- toolName: toolMatch[1],
147973
- status: "completed"
147974
- });
147975
- }
147976
- } else if (content.startsWith("[Step")) {
147977
- const stepMatch = content.match(/\[Step \d+\] (.+)/);
147978
- if (stepMatch) {
147979
- messages.push({
147980
- role: "assistant",
147981
- content: stepMatch[1],
147982
- createdAt
147983
- });
147984
- }
147985
- }
147986
- }
147987
- if (messages.length === 0) {
147988
- return null;
147989
- }
147990
- return {
147991
- id: "discovery-from-logs",
147992
- name: "Attack Surface Discovery",
147993
- type: "attack-surface",
147994
- target: session.targets[0] || "Unknown",
147995
- messages,
147996
- createdAt: new Date(session.time.created),
147997
- status: "completed"
147998
- };
147999
- } catch (e) {
148000
- console.error("Failed to parse logs:", e);
148001
- return null;
148002
- }
148003
- }
148004
- async function loadSessionState(session) {
148005
- const rootPath = session.rootPath;
148006
- let subagents = loadSubagents(rootPath);
148007
- const hasAttackSurfaceAgent = subagents.some((s) => s.type === "attack-surface");
148008
- if (!hasAttackSurfaceAgent) {
148009
- const discoveryAgent = createDiscoveryFromLogs(rootPath, session);
148010
- if (discoveryAgent) {
148011
- subagents = [discoveryAgent, ...subagents];
148012
- }
148013
- }
148014
- const attackSurfaceResults = loadAttackSurfaceResults2(rootPath);
148015
- const hasReportFile = hasReport(rootPath);
148016
- const isComplete = hasReportFile || attackSurfaceResults?.summary?.analysisComplete === true && subagents.length > 1;
148017
- return {
148018
- session,
148019
- subagents,
148020
- attackSurfaceResults,
148021
- isComplete,
148022
- hasReport: hasReportFile
148023
- };
148024
- }
148025
148227
  // src/core/agent/metaTestingAgent/prompts/execution.ts
148026
148228
  var OUTCOME_GUIDANCE_TEMPLATE2 = `{{OUTCOME_GUIDANCE}}`;
148027
148229
  var META_TESTING_SYSTEM_PROMPT = `# Meta Testing Agent
@@ -148473,7 +148675,9 @@ async function runPentestOrchestrator(input) {
148473
148675
  id: agentId,
148474
148676
  name: `${getVulnerabilityClassName(task.vulnClass)} on ${task.target}`,
148475
148677
  target: task.target,
148476
- vulnerabilityClass: task.vulnClass
148678
+ vulnerabilityClass: task.vulnClass,
148679
+ objective: task.objective,
148680
+ authenticationInfo: task.authenticationInfo
148477
148681
  });
148478
148682
  try {
148479
148683
  const result = await runMetaVulnerabilityTestAgent({
@@ -149154,7 +149358,8 @@ async function runStreamlinedPentest(input) {
149154
149358
  onPentestAgentComplete,
149155
149359
  onAgentAbortControllerCreated,
149156
149360
  abortSignal,
149157
- toolOverride
149361
+ toolOverride,
149362
+ previousDiscoveryResults
149158
149363
  } = input;
149159
149364
  const session = input.session || await Session.create({
149160
149365
  targets: [target],
@@ -149162,36 +149367,62 @@ async function runStreamlinedPentest(input) {
149162
149367
  ...sessionConfig
149163
149368
  });
149164
149369
  const logger = new Logger(session, "streamlined-pentest.log");
149370
+ const manifestPath = join18(session.rootPath, "agent-manifest.json");
149371
+ const manifest = (() => {
149372
+ if (!existsSync23(manifestPath))
149373
+ return [];
149374
+ try {
149375
+ return JSON.parse(readFileSync13(manifestPath, "utf-8"));
149376
+ } catch (e) {
149377
+ logger.warn(`Failed to read agent manifest at ${manifestPath}: ${e}`);
149378
+ return [];
149379
+ }
149380
+ })();
149381
+ function flushManifest() {
149382
+ writeFileSync15(manifestPath, JSON.stringify(manifest, null, 2));
149383
+ }
149165
149384
  logger.info(`Starting streamlined pentest for ${target}`);
149166
149385
  const reportProgress = (status) => {
149167
149386
  logger.info(`[${status.phase}] ${status.message}`);
149168
149387
  onProgress?.(status);
149169
149388
  };
149170
149389
  try {
149171
- reportProgress({
149172
- phase: "discovery",
149173
- message: `Discovering attack surface for ${target}`
149174
- });
149175
- const { targets, discoveryError } = await runDiscoveryPhase({
149176
- target,
149177
- model,
149178
- session,
149179
- authConfig,
149180
- abortSignal,
149181
- toolOverride,
149182
- logger,
149183
- onProgress: reportProgress,
149184
- onStepFinish: onDiscoveryStepFinish,
149185
- onStream: onDiscoveryStream
149186
- });
149187
- if (discoveryError) {
149188
- return {
149189
- success: false,
149390
+ let targets;
149391
+ if (previousDiscoveryResults) {
149392
+ logger.info(`Resuming with ${previousDiscoveryResults.targets?.length || 0} previously discovered targets`);
149393
+ targets = previousDiscoveryResults.targets || [];
149394
+ reportProgress({
149395
+ phase: "discovery",
149396
+ message: `Resuming with ${targets.length} previously discovered targets`,
149397
+ targetsDiscovered: targets.length
149398
+ });
149399
+ } else {
149400
+ reportProgress({
149401
+ phase: "discovery",
149402
+ message: `Discovering attack surface for ${target}`
149403
+ });
149404
+ const discoveryResult = await runDiscoveryPhase({
149405
+ target,
149406
+ model,
149190
149407
  session,
149191
- targets: [],
149192
- totalFindings: 0,
149193
- error: discoveryError
149194
- };
149408
+ authConfig,
149409
+ abortSignal,
149410
+ toolOverride,
149411
+ logger,
149412
+ onProgress: reportProgress,
149413
+ onStepFinish: onDiscoveryStepFinish,
149414
+ onStream: onDiscoveryStream
149415
+ });
149416
+ if (discoveryResult.discoveryError) {
149417
+ return {
149418
+ success: false,
149419
+ session,
149420
+ targets: [],
149421
+ totalFindings: 0,
149422
+ error: discoveryResult.discoveryError
149423
+ };
149424
+ }
149425
+ targets = discoveryResult.targets;
149195
149426
  }
149196
149427
  if (targets.length === 0) {
149197
149428
  reportProgress({
@@ -149236,7 +149467,20 @@ async function runStreamlinedPentest(input) {
149236
149467
  findingsCount: status.findingsCount
149237
149468
  });
149238
149469
  },
149239
- onAgentSpawn: onPentestAgentSpawn,
149470
+ onAgentSpawn: (info) => {
149471
+ manifest.push({
149472
+ id: info.id,
149473
+ name: info.name,
149474
+ target: info.target,
149475
+ vulnerabilityClass: info.vulnerabilityClass,
149476
+ objective: info.objective,
149477
+ authInfo: info.authenticationInfo,
149478
+ status: "running",
149479
+ spawnedAt: new Date().toISOString()
149480
+ });
149481
+ flushManifest();
149482
+ onPentestAgentSpawn?.(info);
149483
+ },
149240
149484
  onAgentStream: onPentestAgentStream,
149241
149485
  onAgentComplete: onPentestAgentComplete,
149242
149486
  onAgentAbortControllerCreated
@@ -149462,14 +149706,16 @@ async function runDiscoveryPhase(params) {
149462
149706
  }
149463
149707
 
149464
149708
  // src/tui/components/session-view/index.tsx
149465
- import { existsSync as existsSync24 } from "fs";
149709
+ import { existsSync as existsSync24, readFileSync as readFileSync14 } from "fs";
149466
149710
  import { exec as exec3 } from "child_process";
149711
+ import { join as join19 } from "path";
149467
149712
  var greenBullet6 = RGBA.fromInts(76, 175, 80, 255);
149468
149713
  var creamText6 = RGBA.fromInts(255, 248, 220, 255);
149469
149714
  var dimText7 = RGBA.fromInts(120, 120, 120, 255);
149470
149715
  function SessionView({
149471
149716
  sessionId,
149472
- isResume = false
149717
+ isResume = false,
149718
+ openAsOperator = false
149473
149719
  }) {
149474
149720
  const route = useRoute();
149475
149721
  const { model, setThinking, isExecuting, addTokenUsage, setIsExecuting } = useAgent();
@@ -149482,6 +149728,8 @@ function SessionView({
149482
149728
  const [startTime, setStartTime] = import_react48.useState(null);
149483
149729
  const [hasStarted, setHasStarted] = import_react48.useState(false);
149484
149730
  const agentAbortControllers = import_react48.useRef(new Map);
149731
+ const subagentsRef = import_react48.useRef(subagents);
149732
+ subagentsRef.current = subagents;
149485
149733
  const killAgent = import_react48.useCallback((agentId) => {
149486
149734
  const controller = agentAbortControllers.current.get(agentId);
149487
149735
  if (controller) {
@@ -149524,12 +149772,19 @@ function SessionView({
149524
149772
  target: s.target,
149525
149773
  messages: s.messages,
149526
149774
  createdAt: s.createdAt,
149527
- status: s.status
149775
+ status: s.status,
149776
+ resumeInfo: s.resumeInfo
149528
149777
  }));
149529
149778
  setSubagents(loadedSubagents);
149530
- setIsCompleted(state.hasReport);
149531
149779
  setStartTime(new Date(loadedSession.time.created));
149532
- setHasStarted(true);
149780
+ if (state.hasReport) {
149781
+ setIsCompleted(true);
149782
+ setHasStarted(true);
149783
+ } else if (state.attackSurfaceResults) {
149784
+ setHasStarted(true);
149785
+ } else if (state.interruptedDuringDiscovery) {
149786
+ setHasStarted(true);
149787
+ }
149533
149788
  } catch (e) {
149534
149789
  console.error("Failed to load session state:", e);
149535
149790
  }
@@ -149543,15 +149798,14 @@ function SessionView({
149543
149798
  loadSession();
149544
149799
  }, [sessionId, isResume]);
149545
149800
  import_react48.useEffect(() => {
149546
- if (session && !hasStarted && !loading && !isResume) {
149801
+ if (session && !hasStarted && !loading) {
149547
149802
  const mode = session.config?.mode;
149548
- if (mode === "operator" || mode === "driver") {
149803
+ if (mode === "operator" || mode === "driver" || openAsOperator)
149549
149804
  return;
149550
- }
149551
149805
  setHasStarted(true);
149552
149806
  startPentest(session);
149553
149807
  }
149554
- }, [session, hasStarted, loading, isResume]);
149808
+ }, [session, hasStarted, loading, openAsOperator]);
149555
149809
  import_react48.useEffect(() => {
149556
149810
  return () => {
149557
149811
  if (abortController) {
@@ -149559,7 +149813,7 @@ function SessionView({
149559
149813
  }
149560
149814
  };
149561
149815
  }, [abortController]);
149562
- const startPentest = import_react48.useCallback(async (execSession) => {
149816
+ const startPentest = import_react48.useCallback(async (execSession, previousDiscoveryResults) => {
149563
149817
  setIsExecuting(true);
149564
149818
  setThinking(true);
149565
149819
  setStartTime(new Date);
@@ -149568,23 +149822,36 @@ function SessionView({
149568
149822
  let currentDiscoveryText = "";
149569
149823
  const pentestAgentTexts = new Map;
149570
149824
  try {
149571
- setSubagents([
149572
- {
149573
- id: "attack-surface-discovery",
149574
- name: "Attack Surface Discovery",
149575
- type: "attack-surface",
149576
- target: execSession.targets[0],
149577
- messages: [],
149578
- status: "pending",
149579
- createdAt: new Date
149825
+ setSubagents((prev) => {
149826
+ const existingIdx = prev.findIndex((s) => s.id === "attack-surface-discovery");
149827
+ if (existingIdx !== -1) {
149828
+ const updated = [...prev];
149829
+ updated[existingIdx] = {
149830
+ ...updated[existingIdx],
149831
+ status: previousDiscoveryResults ? "completed" : "pending"
149832
+ };
149833
+ return updated;
149580
149834
  }
149581
- ]);
149835
+ return [
149836
+ ...prev,
149837
+ {
149838
+ id: "attack-surface-discovery",
149839
+ name: "Attack Surface Discovery",
149840
+ type: "attack-surface",
149841
+ target: execSession.targets[0],
149842
+ messages: [],
149843
+ status: "pending",
149844
+ createdAt: new Date
149845
+ }
149846
+ ];
149847
+ });
149582
149848
  const result = await runStreamlinedPentest({
149583
149849
  target: execSession.targets[0],
149584
149850
  model: model.id,
149585
149851
  session: execSession,
149586
149852
  sessionConfig: execSession.config,
149587
149853
  abortSignal: controller.signal,
149854
+ previousDiscoveryResults,
149588
149855
  onAgentAbortControllerCreated: (agentId, abortCtrl) => {
149589
149856
  agentAbortControllers.current.set(agentId, abortCtrl);
149590
149857
  },
@@ -149645,7 +149912,7 @@ function SessionView({
149645
149912
  newMessages[msgIdx] = {
149646
149913
  ...existingMsg,
149647
149914
  status: "completed",
149648
- content: `+ ${description}`,
149915
+ content: description,
149649
149916
  result: tr.output
149650
149917
  };
149651
149918
  } else {
@@ -149749,7 +150016,7 @@ function SessionView({
149749
150016
  newMessages[msgIdx] = {
149750
150017
  ...existingMsg,
149751
150018
  status: "completed",
149752
- content: `+ ${description}`,
150019
+ content: description,
149753
150020
  result: result2
149754
150021
  };
149755
150022
  } else {
@@ -149852,11 +150119,200 @@ function SessionView({
149852
150119
  updated[idx] = { ...subagent, messages: newMessages };
149853
150120
  return updated;
149854
150121
  });
149855
- } else if (event.type === "tool-result") {
149856
- const tr = event.data;
150122
+ } else if (event.type === "tool-result") {
150123
+ const tr = event.data;
150124
+ const toolCallId = tr.toolCallId;
150125
+ const toolName = tr.toolName || "tool";
150126
+ const result2 = tr.output ?? tr.result;
150127
+ setSubagents((prev) => {
150128
+ const idx = prev.findIndex((s) => s.id === agentId);
150129
+ if (idx === -1)
150130
+ return prev;
150131
+ const updated = [...prev];
150132
+ const subagent = updated[idx];
150133
+ const newMessages = [...subagent.messages];
150134
+ const msgIdx = newMessages.findIndex((m2) => m2.role === "tool" && m2.toolCallId === toolCallId);
150135
+ if (msgIdx !== -1) {
150136
+ const existingMsg = newMessages[msgIdx];
150137
+ const description = typeof existingMsg.content === "string" && existingMsg.content !== existingMsg.toolName ? existingMsg.content : existingMsg.toolName || "tool";
150138
+ newMessages[msgIdx] = {
150139
+ ...existingMsg,
150140
+ status: "completed",
150141
+ content: description,
150142
+ result: result2
150143
+ };
150144
+ } else {
150145
+ newMessages.push({
150146
+ role: "tool",
150147
+ status: "completed",
150148
+ toolCallId,
150149
+ toolName,
150150
+ content: `+ ${toolName}`,
150151
+ result: result2,
150152
+ createdAt: new Date
150153
+ });
150154
+ }
150155
+ updated[idx] = { ...subagent, messages: newMessages };
150156
+ return updated;
150157
+ });
150158
+ } else if (event.type === "step-finish" && event.data) {
150159
+ const { usage } = event.data;
150160
+ if (usage) {
150161
+ const stepTokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
150162
+ if (stepTokens > 0)
150163
+ addTokenUsage(usage.inputTokens ?? 0, usage.outputTokens ?? 0);
150164
+ }
150165
+ pentestAgentTexts.set(agentId, "");
150166
+ }
150167
+ },
150168
+ onPentestAgentComplete: (agentId, agentResult) => {
150169
+ setSubagents((prev) => prev.map((sub) => sub.id === agentId && sub.status !== "canceled" ? {
150170
+ ...sub,
150171
+ status: agentResult.error ? "failed" : "completed",
150172
+ messages: [
150173
+ ...sub.messages,
150174
+ {
150175
+ role: "assistant",
150176
+ content: `${agentResult.findingsCount > 0 ? "✅" : "⚪"} ${agentResult.summary}`,
150177
+ createdAt: new Date
150178
+ }
150179
+ ]
150180
+ } : sub));
150181
+ },
150182
+ onProgress: (status) => {}
150183
+ });
150184
+ if (result.success) {
150185
+ if (result.reportPath && existsSync24(result.reportPath) || existsSync24(result.session.rootPath + "/comprehensive-pentest-report.md")) {
150186
+ setIsCompleted(true);
150187
+ }
150188
+ }
150189
+ setThinking(false);
150190
+ setIsExecuting(false);
150191
+ } catch (error42) {
150192
+ setThinking(false);
150193
+ setIsExecuting(false);
150194
+ if (error42 instanceof Error && error42.name === "AbortError") {} else {
150195
+ setError(error42 instanceof Error ? error42.message : "Unknown error occurred");
150196
+ }
150197
+ }
150198
+ }, [model.id, addTokenUsage, setThinking, setIsExecuting]);
150199
+ const resumeAgent = import_react48.useCallback(async (agentId) => {
150200
+ if (!session)
150201
+ return;
150202
+ const paused = subagentsRef.current.find((s) => s.id === agentId && s.status === "paused");
150203
+ if (!paused)
150204
+ return;
150205
+ if (paused.type === "attack-surface") {
150206
+ let previousResults;
150207
+ const resultsPath = join19(session.rootPath, "attack-surface-results.json");
150208
+ if (existsSync24(resultsPath)) {
150209
+ try {
150210
+ previousResults = JSON.parse(readFileSync14(resultsPath, "utf-8"));
150211
+ } catch {}
150212
+ }
150213
+ await startPentest(session, previousResults);
150214
+ return;
150215
+ }
150216
+ if (!paused.resumeInfo)
150217
+ return;
150218
+ const { target, objective, vulnerabilityClass, authenticationInfo } = paused.resumeInfo;
150219
+ setSubagents((prev) => prev.map((s) => s.id === agentId ? { ...s, status: "pending" } : s));
150220
+ setIsExecuting(true);
150221
+ setThinking(true);
150222
+ let accumulatedText = "";
150223
+ try {
150224
+ const result = await runMetaVulnerabilityTestAgent({
150225
+ input: {
150226
+ target,
150227
+ objective,
150228
+ vulnerabilityClass,
150229
+ authenticationInfo,
150230
+ authenticationInstructions: session.config?.authenticationInstructions,
150231
+ outcomeGuidance: session.config?.outcomeGuidance || "Find and document vulnerabilities",
150232
+ session: {
150233
+ id: session.id,
150234
+ rootPath: session.rootPath,
150235
+ findingsPath: session.findingsPath,
150236
+ logsPath: session.logsPath,
150237
+ pocsPath: session.rootPath + "/pocs"
150238
+ },
150239
+ sessionConfig: {
150240
+ enableCvssScoring: session.config?.enableCvssScoring,
150241
+ cvssModel: session.config?.cvssModel
150242
+ }
150243
+ },
150244
+ model: model.id,
150245
+ abortSignal: abortController?.signal,
150246
+ onChunk: (chunk) => {
150247
+ if (chunk.type === "text-delta" && chunk.text) {
150248
+ accumulatedText += chunk.text;
150249
+ setThinking(false);
150250
+ if (accumulatedText.trim()) {
150251
+ setSubagents((prev) => {
150252
+ const idx = prev.findIndex((s) => s.id === agentId);
150253
+ if (idx === -1)
150254
+ return prev;
150255
+ const updated = [...prev];
150256
+ const subagent = updated[idx];
150257
+ const lastMsg = subagent.messages[subagent.messages.length - 1];
150258
+ if (lastMsg && lastMsg.role === "assistant") {
150259
+ const newMessages = [...subagent.messages];
150260
+ newMessages[newMessages.length - 1] = {
150261
+ ...lastMsg,
150262
+ content: accumulatedText
150263
+ };
150264
+ updated[idx] = { ...subagent, messages: newMessages };
150265
+ } else {
150266
+ updated[idx] = {
150267
+ ...subagent,
150268
+ messages: [
150269
+ ...subagent.messages,
150270
+ {
150271
+ role: "assistant",
150272
+ content: accumulatedText,
150273
+ createdAt: new Date
150274
+ }
150275
+ ]
150276
+ };
150277
+ }
150278
+ return updated;
150279
+ });
150280
+ }
150281
+ } else if (chunk.type === "tool-call") {
150282
+ setThinking(false);
150283
+ const tc = chunk;
150284
+ const toolCallId = tc.toolCallId;
150285
+ const toolName = tc.toolName || "tool";
150286
+ const args = tc.input ?? tc.args;
150287
+ const toolDescription = typeof args?.toolCallDescription === "string" ? args.toolCallDescription : toolName;
150288
+ setSubagents((prev) => {
150289
+ const idx = prev.findIndex((s) => s.id === agentId);
150290
+ if (idx === -1)
150291
+ return prev;
150292
+ const updated = [...prev];
150293
+ const subagent = updated[idx];
150294
+ const newMessages = [...subagent.messages];
150295
+ const exists = newMessages.some((m2) => m2.role === "tool" && m2.toolCallId === toolCallId);
150296
+ if (!exists) {
150297
+ newMessages.push({
150298
+ role: "tool",
150299
+ status: "pending",
150300
+ toolCallId,
150301
+ toolName,
150302
+ content: toolDescription,
150303
+ args,
150304
+ createdAt: new Date
150305
+ });
150306
+ }
150307
+ updated[idx] = { ...subagent, messages: newMessages };
150308
+ return updated;
150309
+ });
150310
+ } else if (chunk.type === "tool-result") {
150311
+ setThinking(true);
150312
+ const tr = chunk;
149857
150313
  const toolCallId = tr.toolCallId;
149858
150314
  const toolName = tr.toolName || "tool";
149859
- const result2 = tr.output ?? tr.result;
150315
+ const resultData = tr.output ?? tr.result;
149860
150316
  setSubagents((prev) => {
149861
150317
  const idx = prev.findIndex((s) => s.id === agentId);
149862
150318
  if (idx === -1)
@@ -149871,8 +150327,8 @@ function SessionView({
149871
150327
  newMessages[msgIdx] = {
149872
150328
  ...existingMsg,
149873
150329
  status: "completed",
149874
- content: `+ ${description}`,
149875
- result: result2
150330
+ content: description,
150331
+ result: resultData
149876
150332
  };
149877
150333
  } else {
149878
150334
  newMessages.push({
@@ -149881,54 +150337,47 @@ function SessionView({
149881
150337
  toolCallId,
149882
150338
  toolName,
149883
150339
  content: `+ ${toolName}`,
149884
- result: result2,
150340
+ result: resultData,
149885
150341
  createdAt: new Date
149886
150342
  });
149887
150343
  }
149888
150344
  updated[idx] = { ...subagent, messages: newMessages };
149889
150345
  return updated;
149890
150346
  });
149891
- } else if (event.type === "step-finish" && event.data) {
149892
- const { usage } = event.data;
149893
- if (usage) {
149894
- const stepTokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
149895
- if (stepTokens > 0)
149896
- addTokenUsage(usage.inputTokens ?? 0, usage.outputTokens ?? 0);
149897
- }
149898
- pentestAgentTexts.set(agentId, "");
149899
150347
  }
149900
150348
  },
149901
- onPentestAgentComplete: (agentId, agentResult) => {
149902
- setSubagents((prev) => prev.map((sub) => sub.id === agentId && sub.status !== "canceled" ? {
149903
- ...sub,
149904
- status: agentResult.error ? "failed" : "completed",
149905
- messages: [
149906
- ...sub.messages,
149907
- {
149908
- role: "assistant",
149909
- content: `${agentResult.findingsCount > 0 ? "✅" : "⚪"} ${agentResult.summary}`,
149910
- createdAt: new Date
149911
- }
149912
- ]
149913
- } : sub));
149914
- },
149915
- onProgress: (status) => {}
149916
- });
149917
- if (result.success) {
149918
- if (result.reportPath && existsSync24(result.reportPath) || existsSync24(result.session.rootPath + "/comprehensive-pentest-report.md")) {
149919
- setIsCompleted(true);
150349
+ onStepFinish: (step) => {
150350
+ const usage = step.usage;
150351
+ if (usage) {
150352
+ const stepTokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
150353
+ if (stepTokens > 0)
150354
+ addTokenUsage(usage.inputTokens ?? 0, usage.outputTokens ?? 0);
150355
+ }
150356
+ accumulatedText = "";
149920
150357
  }
149921
- }
149922
- setThinking(false);
149923
- setIsExecuting(false);
150358
+ });
150359
+ setSubagents((prev) => prev.map((s) => s.id === agentId ? {
150360
+ ...s,
150361
+ status: result.error ? "failed" : "completed",
150362
+ resumeInfo: undefined,
150363
+ messages: [
150364
+ ...s.messages,
150365
+ {
150366
+ role: "assistant",
150367
+ content: `${result.findingsCount > 0 ? "✅" : "⚪"} ${result.summary}`,
150368
+ createdAt: new Date
150369
+ }
150370
+ ]
150371
+ } : s));
149924
150372
  } catch (error42) {
149925
- setThinking(false);
150373
+ setSubagents((prev) => prev.map((s) => s.id === agentId ? { ...s, status: "failed", resumeInfo: undefined } : s));
150374
+ }
150375
+ setThinking(false);
150376
+ const stillRunning = subagentsRef.current.some((s) => s.status === "pending" && s.id !== agentId);
150377
+ if (!stillRunning) {
149926
150378
  setIsExecuting(false);
149927
- if (error42 instanceof Error && error42.name === "AbortError") {} else {
149928
- setError(error42 instanceof Error ? error42.message : "Unknown error occurred");
149929
- }
149930
150379
  }
149931
- }, [model.id, addTokenUsage, setThinking, setIsExecuting]);
150380
+ }, [session, model.id, abortController, addTokenUsage, setThinking, setIsExecuting, startPentest]);
149932
150381
  const openReport = import_react48.useCallback(() => {
149933
150382
  if (session?.rootPath) {
149934
150383
  const reportPath = `${session.rootPath}/comprehensive-pentest-report.md`;
@@ -149989,10 +150438,11 @@ function SessionView({
149989
150438
  session
149990
150439
  }, undefined, false, undefined, this);
149991
150440
  }
149992
- if (session.config?.mode === "operator") {
150441
+ if (session.config?.mode === "operator" || openAsOperator) {
149993
150442
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(OperatorDashboard, {
149994
150443
  session,
149995
- isResume
150444
+ isResume,
150445
+ openAsOperator
149996
150446
  }, undefined, false, undefined, this);
149997
150447
  }
149998
150448
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SwarmDashboard, {
@@ -150003,7 +150453,8 @@ function SessionView({
150003
150453
  isCompleted,
150004
150454
  onBack: handleBack,
150005
150455
  onViewReport: openReport,
150006
- onKillAgent: killAgent
150456
+ onKillAgent: killAgent,
150457
+ onResumeAgent: resumeAgent
150007
150458
  }, undefined, false, undefined, this);
150008
150459
  }
150009
150460
 
@@ -150752,10 +151203,17 @@ function HomeView({ onNavigate, onStartSession }) {
150752
151203
  const { executeCommand, autocompleteOptions } = useCommand();
150753
151204
  const { setInputValue } = useInput();
150754
151205
  const { promptRef } = useFocus();
151206
+ const [hintMessage, setHintMessage] = import_react56.useState(null);
150755
151207
  const handleSubmit = import_react56.useCallback((value) => {
150756
- onStartSession(value);
151208
+ setHintMessage("Type /help to get started");
150757
151209
  setInputValue("");
150758
- }, [onStartSession, setInputValue]);
151210
+ }, [setInputValue]);
151211
+ import_react56.useEffect(() => {
151212
+ if (!hintMessage)
151213
+ return;
151214
+ const timer = setTimeout(() => setHintMessage(null), 3000);
151215
+ return () => clearTimeout(timer);
151216
+ }, [hintMessage]);
150759
151217
  const handleCommandExecute = import_react56.useCallback(async (command) => {
150760
151218
  await executeCommand(command);
150761
151219
  }, [executeCommand]);
@@ -150825,6 +151283,13 @@ function HomeView({ onNavigate, onStartSession }) {
150825
151283
  onCommandExecute: handleCommandExecute,
150826
151284
  showPromptIndicator: true
150827
151285
  }, undefined, false, undefined, this),
151286
+ hintMessage && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
151287
+ marginTop: 1,
151288
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
151289
+ fg: creamText7,
151290
+ children: hintMessage
151291
+ }, undefined, false, undefined, this)
151292
+ }, undefined, false, undefined, this),
150828
151293
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
150829
151294
  marginTop: 1,
150830
151295
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
@@ -153021,13 +153486,27 @@ function WebWizard({
153021
153486
  }, undefined, true, undefined, this);
153022
153487
  }
153023
153488
 
153024
- // src/tui/components/commands/resume-wizard.tsx
153489
+ // src/tui/components/commands/sessions-browser.tsx
153490
+ var import_react69 = __toESM(require_react(), 1);
153491
+ import { exec as exec5 } from "child_process";
153492
+ import { existsSync as existsSync27 } from "fs";
153493
+
153494
+ // src/tui/hooks/use-sessions-list.ts
153025
153495
  var import_react68 = __toESM(require_react(), 1);
153026
153496
  import { existsSync as existsSync26, readdirSync as readdirSync7 } from "fs";
153027
- import { join as join19 } from "path";
153028
- var greenAccent5 = RGBA.fromInts(76, 175, 80, 255);
153029
- var creamText12 = RGBA.fromInts(255, 248, 220, 255);
153030
- var dimText13 = RGBA.fromInts(120, 120, 120, 255);
153497
+ import { join as join20 } from "path";
153498
+ function countFindings(findingsPath) {
153499
+ try {
153500
+ if (!existsSync26(findingsPath))
153501
+ return 0;
153502
+ return readdirSync7(findingsPath).filter((f) => f.endsWith(".json")).length;
153503
+ } catch {
153504
+ return 0;
153505
+ }
153506
+ }
153507
+ function checkHasReport(rootPath) {
153508
+ return existsSync26(join20(rootPath, "comprehensive-pentest-report.md"));
153509
+ }
153031
153510
  function formatRelativeTime(timestamp) {
153032
153511
  const now2 = Date.now();
153033
153512
  const diff = now2 - timestamp;
@@ -153046,89 +153525,177 @@ function formatRelativeTime(timestamp) {
153046
153525
  return `${days}d ago`;
153047
153526
  return new Date(timestamp).toLocaleDateString();
153048
153527
  }
153049
- function countFindings(findingsPath) {
153050
- try {
153051
- if (!existsSync26(findingsPath))
153052
- return 0;
153053
- return readdirSync7(findingsPath).filter((f) => f.endsWith(".json")).length;
153054
- } catch {
153055
- return 0;
153056
- }
153057
- }
153058
- function ResumeWizard() {
153528
+ function useSessionsList() {
153059
153529
  const [sessions, setSessions] = import_react68.useState([]);
153060
- const [selectedIndex, setSelectedIndex] = import_react68.useState(0);
153061
153530
  const [loading, setLoading] = import_react68.useState(true);
153062
- const [statusMessage, setStatusMessage] = import_react68.useState("");
153531
+ const [searchTerm, setSearchTerm] = import_react68.useState("");
153532
+ const loadSessions = import_react68.useCallback(async () => {
153533
+ setLoading(true);
153534
+ try {
153535
+ const enriched = [];
153536
+ for await (const session of Session.list()) {
153537
+ const hasOperatorState = existsSync26(join20(session.rootPath, "operator-state.json"));
153538
+ const findingsCount = countFindings(session.findingsPath);
153539
+ const hasReport2 = checkHasReport(session.rootPath);
153540
+ enriched.push({ ...session, findingsCount, hasOperatorState, hasReport: hasReport2 });
153541
+ }
153542
+ enriched.sort((a, b2) => b2.time.updated - a.time.updated);
153543
+ setSessions(enriched);
153544
+ } catch (error41) {
153545
+ console.error("Error loading sessions:", error41);
153546
+ } finally {
153547
+ setLoading(false);
153548
+ }
153549
+ }, []);
153550
+ import_react68.useEffect(() => {
153551
+ loadSessions();
153552
+ }, [loadSessions]);
153553
+ const deleteSession = import_react68.useCallback(async (id) => {
153554
+ await Session.remove({ sessionId: id });
153555
+ await loadSessions();
153556
+ }, [loadSessions]);
153557
+ const filtered = searchTerm ? sessions.filter((s) => s.name.toLowerCase().includes(searchTerm.toLowerCase())) : sessions;
153558
+ const groupsMap = new Map;
153559
+ for (const session of filtered) {
153560
+ const d2 = new Date(session.time.created);
153561
+ const dateStr = d2.toLocaleDateString("en-US", {
153562
+ weekday: "short",
153563
+ month: "short",
153564
+ day: "numeric",
153565
+ year: "numeric"
153566
+ });
153567
+ let group = groupsMap.get(dateStr);
153568
+ if (!group) {
153569
+ group = { date: dateStr, timestamp: d2.getTime(), sessions: [] };
153570
+ groupsMap.set(dateStr, group);
153571
+ }
153572
+ group.sessions.push(session);
153573
+ }
153574
+ const rawGroups = Array.from(groupsMap.values());
153575
+ rawGroups.sort((a, b2) => b2.timestamp - a.timestamp);
153576
+ for (const g2 of rawGroups) {
153577
+ g2.sessions.sort((a, b2) => new Date(b2.time.created).getTime() - new Date(a.time.created).getTime());
153578
+ }
153579
+ const visualOrderSessions = [];
153580
+ const groupedSessions = [];
153581
+ let idx = 0;
153582
+ for (const raw of rawGroups) {
153583
+ const group = { date: raw.date, timestamp: raw.timestamp, sessions: [] };
153584
+ for (const s of raw.sessions) {
153585
+ visualOrderSessions.push(s);
153586
+ group.sessions.push({ ...s, index: idx });
153587
+ idx++;
153588
+ }
153589
+ groupedSessions.push(group);
153590
+ }
153591
+ return {
153592
+ sessions: filtered,
153593
+ groupedSessions,
153594
+ visualOrderSessions,
153595
+ loading,
153596
+ searchTerm,
153597
+ setSearchTerm,
153598
+ deleteSession,
153599
+ reload: loadSessions
153600
+ };
153601
+ }
153602
+
153603
+ // src/tui/components/commands/sessions-browser.tsx
153604
+ var greenAccent5 = RGBA.fromInts(76, 175, 80, 255);
153605
+ var creamText12 = RGBA.fromInts(255, 248, 220, 255);
153606
+ var dimText13 = RGBA.fromInts(120, 120, 120, 255);
153607
+ function SessionsBrowser() {
153063
153608
  const route = useRoute();
153064
153609
  const { load: loadSession } = useSession();
153065
153610
  const { refocusPrompt } = useFocus();
153066
- import_react68.useEffect(() => {
153067
- async function loadOperatorSessions() {
153068
- setLoading(true);
153069
- try {
153070
- const enrichedSessions = [];
153071
- for await (const session of Session.list()) {
153072
- const statePath = join19(session.rootPath, "operator-state.json");
153073
- const hasOperatorState = existsSync26(statePath);
153074
- const findingsCount = countFindings(session.findingsPath);
153075
- enrichedSessions.push({
153076
- ...session,
153077
- findingsCount,
153078
- hasOperatorState
153079
- });
153080
- }
153081
- enrichedSessions.sort((a, b2) => b2.time.updated - a.time.updated);
153082
- setSessions(enrichedSessions.slice(0, 20));
153083
- } catch (error41) {
153084
- console.error("Error loading sessions:", error41);
153085
- setStatusMessage("Error loading sessions");
153086
- } finally {
153087
- setLoading(false);
153088
- }
153611
+ const {
153612
+ groupedSessions,
153613
+ visualOrderSessions,
153614
+ loading,
153615
+ searchTerm,
153616
+ setSearchTerm,
153617
+ deleteSession,
153618
+ reload
153619
+ } = useSessionsList();
153620
+ const [selectedIndex, setSelectedIndex] = import_react69.useState(0);
153621
+ const [statusMessage, setStatusMessage] = import_react69.useState("");
153622
+ const scroll = import_react69.useRef(null);
153623
+ import_react69.useEffect(() => {
153624
+ if (visualOrderSessions.length > 0 && selectedIndex >= visualOrderSessions.length) {
153625
+ setSelectedIndex(visualOrderSessions.length - 1);
153626
+ } else if (visualOrderSessions.length === 0) {
153627
+ setSelectedIndex(0);
153089
153628
  }
153090
- loadOperatorSessions();
153629
+ }, [visualOrderSessions.length, selectedIndex]);
153630
+ const showStatus = import_react69.useCallback((msg) => {
153631
+ setStatusMessage(msg);
153632
+ setTimeout(() => setStatusMessage(""), 2000);
153091
153633
  }, []);
153092
- const resumeSession = async (session) => {
153093
- try {
153094
- const loaded = await loadSession(session.id);
153095
- if (!loaded) {
153096
- setStatusMessage("Error loading session");
153097
- setTimeout(() => setStatusMessage(""), 2000);
153098
- return;
153099
- }
153100
- route.navigate({
153101
- type: "session",
153102
- sessionId: session.id,
153103
- isResume: true
153104
- });
153105
- } catch (error41) {
153106
- console.error("Error resuming session:", error41);
153107
- setStatusMessage("Error resuming session");
153108
- setTimeout(() => setStatusMessage(""), 2000);
153634
+ const openSession = import_react69.useCallback(async (session, asOperator) => {
153635
+ const loaded = await loadSession(session.id);
153636
+ if (!loaded) {
153637
+ showStatus("Error loading session");
153638
+ return;
153109
153639
  }
153110
- };
153111
- useKeyboard((key) => {
153640
+ route.navigate({
153641
+ type: "session",
153642
+ sessionId: session.id,
153643
+ isResume: true,
153644
+ openAsOperator: asOperator || undefined
153645
+ });
153646
+ }, [loadSession, route, showStatus]);
153647
+ const openReport = import_react69.useCallback(async (session) => {
153648
+ const reportPath = await Storage.locate([session.id, "pentest-report"], ".md");
153649
+ if (!existsSync27(reportPath)) {
153650
+ showStatus("Report not found");
153651
+ return;
153652
+ }
153653
+ exec5(`open "${reportPath}"`, (error41) => {
153654
+ if (error41)
153655
+ showStatus("Error opening report");
153656
+ });
153657
+ }, [showStatus]);
153658
+ useKeyboard(async (key) => {
153112
153659
  if (key.name === "escape") {
153113
153660
  refocusPrompt();
153114
153661
  route.navigate({ type: "base", path: "home" });
153115
153662
  return;
153116
153663
  }
153117
- if (key.name === "up" && sessions.length > 0) {
153118
- setSelectedIndex((i) => i > 0 ? i - 1 : sessions.length - 1);
153664
+ if (key.name === "return" && visualOrderSessions.length > 0) {
153665
+ key.preventDefault();
153666
+ const selected = visualOrderSessions[selectedIndex];
153667
+ if (selected)
153668
+ await openSession(selected, false);
153669
+ return;
153670
+ }
153671
+ if ((key.name === "o" || key.name === "O") && !key.ctrl && !key.meta && visualOrderSessions.length > 0) {
153672
+ const selected = visualOrderSessions[selectedIndex];
153673
+ if (selected)
153674
+ await openSession(selected, true);
153119
153675
  return;
153120
153676
  }
153121
- if (key.name === "down" && sessions.length > 0) {
153122
- setSelectedIndex((i) => i < sessions.length - 1 ? i + 1 : 0);
153677
+ if ((key.name === "r" || key.name === "R") && !key.ctrl && !key.meta && visualOrderSessions.length > 0) {
153678
+ const selected = visualOrderSessions[selectedIndex];
153679
+ if (selected)
153680
+ await openReport(selected);
153123
153681
  return;
153124
153682
  }
153125
- if (key.name === "return" && sessions.length > 0) {
153126
- const selected = sessions[selectedIndex];
153683
+ if (key.ctrl && key.name === "d" && visualOrderSessions.length > 0) {
153684
+ const selected = visualOrderSessions[selectedIndex];
153127
153685
  if (selected) {
153128
- resumeSession(selected);
153686
+ await deleteSession(selected.id);
153687
+ showStatus("Session deleted");
153129
153688
  }
153130
153689
  return;
153131
153690
  }
153691
+ if (key.name === "up" && visualOrderSessions.length > 0) {
153692
+ setSelectedIndex((i) => i > 0 ? i - 1 : visualOrderSessions.length - 1);
153693
+ return;
153694
+ }
153695
+ if (key.name === "down" && visualOrderSessions.length > 0) {
153696
+ setSelectedIndex((i) => i < visualOrderSessions.length - 1 ? i + 1 : 0);
153697
+ return;
153698
+ }
153132
153699
  });
153133
153700
  if (loading) {
153134
153701
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
@@ -153141,7 +153708,7 @@ function ResumeWizard() {
153141
153708
  }, undefined, false, undefined, this)
153142
153709
  }, undefined, false, undefined, this);
153143
153710
  }
153144
- if (sessions.length === 0) {
153711
+ if (visualOrderSessions.length === 0) {
153145
153712
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153146
153713
  flexDirection: "column",
153147
153714
  padding: 2,
@@ -153150,15 +153717,15 @@ function ResumeWizard() {
153150
153717
  children: [
153151
153718
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153152
153719
  fg: creamText12,
153153
- children: "Resume Pentest Session"
153720
+ children: "Sessions"
153154
153721
  }, undefined, false, undefined, this),
153155
153722
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153156
153723
  fg: dimText13,
153157
- children: "No sessions found to resume."
153724
+ children: "No sessions found."
153158
153725
  }, undefined, false, undefined, this),
153159
153726
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153160
153727
  fg: dimText13,
153161
- children: "Start a new session with /web or /operator"
153728
+ children: "Start a new session with /pentest or /operator"
153162
153729
  }, undefined, false, undefined, this),
153163
153730
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153164
153731
  fg: dimText13,
@@ -153172,98 +153739,142 @@ function ResumeWizard() {
153172
153739
  padding: 2,
153173
153740
  gap: 1,
153174
153741
  width: "100%",
153742
+ flexGrow: 1,
153175
153743
  children: [
153176
153744
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153177
153745
  fg: creamText12,
153178
- children: "Resume Pentest Session"
153746
+ children: "Sessions"
153179
153747
  }, undefined, false, undefined, this),
153180
153748
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153181
153749
  fg: dimText13,
153182
- children: "Select a session to continue where you left off"
153750
+ children: "Browse and reopen previous pentest sessions"
153183
153751
  }, undefined, false, undefined, this),
153184
153752
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153185
- flexDirection: "column",
153186
- marginTop: 1,
153187
- children: sessions.map((session, index) => {
153188
- const isSelected = index === selectedIndex;
153189
- const age = formatRelativeTime(session.time.updated);
153190
- const target = session.targets[0] || "No target";
153191
- const findingsText = session.findingsCount > 0 ? `${session.findingsCount} finding${session.findingsCount > 1 ? "s" : ""}` : "No findings";
153192
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153193
- flexDirection: "row",
153194
- justifyContent: "space-between",
153195
- width: "100%",
153196
- border: isSelected ? ["left"] : undefined,
153197
- borderColor: isSelected ? greenAccent5 : undefined,
153198
- paddingLeft: isSelected ? 1 : 2,
153199
- children: [
153200
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153201
- flexDirection: "row",
153202
- gap: 1,
153203
- children: [
153204
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153205
- fg: isSelected ? greenAccent5 : creamText12,
153206
- children: [
153207
- isSelected ? "▸ " : " ",
153208
- session.name
153209
- ]
153210
- }, undefined, true, undefined, this),
153211
- session.hasOperatorState && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153212
- fg: greenAccent5,
153213
- children: "●"
153214
- }, undefined, false, undefined, this)
153215
- ]
153216
- }, undefined, true, undefined, this),
153217
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153218
- fg: dimText13,
153219
- children: [
153220
- target,
153221
- " · ",
153222
- findingsText,
153223
- " · ",
153224
- age
153225
- ]
153226
- }, undefined, true, undefined, this)
153227
- ]
153228
- }, session.id, true, undefined, this);
153229
- })
153753
+ width: "100%",
153754
+ border: ["left"],
153755
+ borderColor: greenAccent5,
153756
+ backgroundColor: "transparent",
153757
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
153758
+ paddingLeft: 1,
153759
+ backgroundColor: "transparent",
153760
+ placeholder: "Search sessions...",
153761
+ value: searchTerm,
153762
+ onInput: setSearchTerm,
153763
+ focused: true
153764
+ }, undefined, false, undefined, this)
153230
153765
  }, undefined, false, undefined, this),
153231
153766
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153232
- marginTop: 2,
153233
153767
  flexDirection: "column",
153234
- children: [
153235
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153236
- fg: dimText13,
153237
- children: [
153238
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153239
- fg: greenAccent5,
153240
- children: "↑↓"
153241
- }, undefined, false, undefined, this),
153242
- " Navigate ",
153243
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153244
- fg: greenAccent5,
153245
- children: "Enter"
153246
- }, undefined, false, undefined, this),
153247
- " Resume ",
153248
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153249
- fg: greenAccent5,
153250
- children: "Esc"
153251
- }, undefined, false, undefined, this),
153252
- " Cancel"
153253
- ]
153254
- }, undefined, true, undefined, this),
153255
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153256
- fg: dimText13,
153768
+ gap: 1,
153769
+ flexGrow: 1,
153770
+ overflow: "hidden",
153771
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("scrollbox", {
153772
+ ref: scroll,
153773
+ scrollbarOptions: { visible: false },
153774
+ style: {
153775
+ rootOptions: {
153776
+ width: "100%",
153777
+ flexGrow: 1,
153778
+ flexShrink: 1,
153779
+ overflow: "hidden"
153780
+ },
153781
+ wrapperOptions: { overflow: "hidden" },
153782
+ contentOptions: { gap: 2, flexDirection: "column" }
153783
+ },
153784
+ children: groupedSessions.map((group) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153785
+ flexDirection: "column",
153786
+ gap: 1,
153257
153787
  children: [
153258
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153788
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153259
153789
  fg: greenAccent5,
153260
- children: "●"
153790
+ children: group.date
153261
153791
  }, undefined, false, undefined, this),
153262
- " = Has saved state (full context restore)"
153792
+ group.sessions.map((session) => {
153793
+ const isSelected = session.index === selectedIndex;
153794
+ const age = formatRelativeTime(session.time.updated);
153795
+ const mode = session.config?.mode || "auto";
153796
+ const modeBadge = mode === "operator" ? "[operator]" : "[auto]";
153797
+ const findingsText = session.findingsCount > 0 ? `${session.findingsCount} finding${session.findingsCount > 1 ? "s" : ""}` : "";
153798
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153799
+ id: session.id,
153800
+ backgroundColor: "transparent",
153801
+ border: isSelected ? ["left"] : undefined,
153802
+ borderColor: isSelected ? greenAccent5 : undefined,
153803
+ paddingLeft: isSelected ? 1 : 2,
153804
+ flexDirection: "row",
153805
+ justifyContent: "space-between",
153806
+ width: "100%",
153807
+ children: [
153808
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153809
+ flexDirection: "row",
153810
+ gap: 1,
153811
+ children: [
153812
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153813
+ fg: isSelected ? greenAccent5 : creamText12,
153814
+ children: [
153815
+ isSelected ? "▸ " : " ",
153816
+ session.name
153817
+ ]
153818
+ }, undefined, true, undefined, this),
153819
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153820
+ fg: mode === "operator" ? greenAccent5 : dimText13,
153821
+ children: modeBadge
153822
+ }, undefined, false, undefined, this),
153823
+ findingsText ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153824
+ fg: dimText13,
153825
+ children: findingsText
153826
+ }, undefined, false, undefined, this) : null
153827
+ ]
153828
+ }, undefined, true, undefined, this),
153829
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153830
+ fg: dimText13,
153831
+ children: age
153832
+ }, undefined, false, undefined, this)
153833
+ ]
153834
+ }, session.id, true, undefined, this);
153835
+ })
153263
153836
  ]
153264
- }, undefined, true, undefined, this)
153265
- ]
153266
- }, undefined, true, undefined, this),
153837
+ }, group.date, true, undefined, this))
153838
+ }, undefined, false, undefined, this)
153839
+ }, undefined, false, undefined, this),
153840
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
153841
+ flexDirection: "row",
153842
+ gap: 1,
153843
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153844
+ fg: dimText13,
153845
+ children: [
153846
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153847
+ fg: greenAccent5,
153848
+ children: "[Enter]"
153849
+ }, undefined, false, undefined, this),
153850
+ " Open",
153851
+ " ",
153852
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153853
+ fg: greenAccent5,
153854
+ children: "[O]"
153855
+ }, undefined, false, undefined, this),
153856
+ " Operator",
153857
+ " ",
153858
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153859
+ fg: greenAccent5,
153860
+ children: "[R]"
153861
+ }, undefined, false, undefined, this),
153862
+ " Report",
153863
+ " ",
153864
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153865
+ fg: greenAccent5,
153866
+ children: "[Ctrl+D]"
153867
+ }, undefined, false, undefined, this),
153868
+ " Delete",
153869
+ " ",
153870
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
153871
+ fg: greenAccent5,
153872
+ children: "[Esc]"
153873
+ }, undefined, false, undefined, this),
153874
+ " Back"
153875
+ ]
153876
+ }, undefined, true, undefined, this)
153877
+ }, undefined, false, undefined, this),
153267
153878
  statusMessage && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
153268
153879
  fg: greenAccent5,
153269
153880
  children: statusMessage
@@ -153273,16 +153884,16 @@ function ResumeWizard() {
153273
153884
  }
153274
153885
 
153275
153886
  // src/tui/components/commands/provider-manager.tsx
153276
- var import_react74 = __toESM(require_react(), 1);
153887
+ var import_react75 = __toESM(require_react(), 1);
153277
153888
  // src/tui/components/commands/provider-selection.tsx
153278
- var import_react71 = __toESM(require_react(), 1);
153889
+ var import_react72 = __toESM(require_react(), 1);
153279
153890
  function ProviderSelection({
153280
153891
  onProviderSelected,
153281
153892
  onClose
153282
153893
  }) {
153283
153894
  const route = useRoute();
153284
153895
  const _config = useConfig();
153285
- const [highlightedIndex, setHighlightedIndex] = import_react71.useState(0);
153896
+ const [highlightedIndex, setHighlightedIndex] = import_react72.useState(0);
153286
153897
  const configuredProviders = getConfiguredProviders(_config.data);
153287
153898
  useKeyboard((key) => {
153288
153899
  if (key.name === "escape") {
@@ -153435,14 +154046,14 @@ function ProviderSelection({
153435
154046
  }
153436
154047
 
153437
154048
  // src/tui/components/commands/api-key-input.tsx
153438
- var import_react73 = __toESM(require_react(), 1);
154049
+ var import_react74 = __toESM(require_react(), 1);
153439
154050
  function APIKeyInput({
153440
154051
  provider,
153441
154052
  providerName,
153442
154053
  onSubmit,
153443
154054
  onCancel
153444
154055
  }) {
153445
- const [apiKey, setApiKey] = import_react73.useState("");
154056
+ const [apiKey, setApiKey] = import_react74.useState("");
153446
154057
  useKeyboard((key) => {
153447
154058
  if (key.name === "escape") {
153448
154059
  onCancel();
@@ -153554,8 +154165,8 @@ function APIKeyInput({
153554
154165
  function ProviderManager() {
153555
154166
  const route = useRoute();
153556
154167
  const _config = useConfig();
153557
- const [flowState, setFlowState] = import_react74.useState("selecting");
153558
- const [selectedProvider, setSelectedProvider] = import_react74.useState(null);
154168
+ const [flowState, setFlowState] = import_react75.useState("selecting");
154169
+ const [selectedProvider, setSelectedProvider] = import_react75.useState(null);
153559
154170
  const handleProviderSelected = (providerId) => {
153560
154171
  setSelectedProvider(providerId);
153561
154172
  setFlowState("inputting");
@@ -153613,7 +154224,7 @@ function ProviderManager() {
153613
154224
  }
153614
154225
 
153615
154226
  // src/tui/components/switch.tsx
153616
- var import_react75 = __toESM(require_react(), 1);
154227
+ var import_react76 = __toESM(require_react(), 1);
153617
154228
  var CaseSymbol = Symbol("Switch.Case");
153618
154229
  var DefaultSymbol = Symbol("Switch.Default");
153619
154230
  function CaseComponent({ children }) {
@@ -153634,8 +154245,8 @@ function SwitchComponent({
153634
154245
  }) {
153635
154246
  let matchedChild = null;
153636
154247
  let defaultChild = null;
153637
- import_react75.default.Children.forEach(children, (child) => {
153638
- if (import_react75.default.isValidElement(child)) {
154248
+ import_react76.default.Children.forEach(children, (child) => {
154249
+ if (import_react76.default.isValidElement(child)) {
153639
154250
  if (child.type[CaseSymbol]) {
153640
154251
  const caseChild = child;
153641
154252
  if (caseChild.props.when === condition) {
@@ -153847,7 +154458,7 @@ function ShortcutsDialog({ open, onClose }) {
153847
154458
  }
153848
154459
 
153849
154460
  // src/tui/components/commands/help-dialog.tsx
153850
- var import_react78 = __toESM(require_react(), 1);
154461
+ var import_react79 = __toESM(require_react(), 1);
153851
154462
  var bgOverlay2 = RGBA.fromInts(0, 0, 0, 200);
153852
154463
  var bgPanel2 = RGBA.fromInts(20, 20, 20, 255);
153853
154464
  var borderColor3 = RGBA.fromInts(60, 60, 60, 255);
@@ -153859,10 +154470,10 @@ function HelpDialog() {
153859
154470
  const { commands: commands2 } = useCommand();
153860
154471
  const route = useRoute();
153861
154472
  const dimensions = useTerminalDimensions();
153862
- const [selectedIndex, setSelectedIndex] = import_react78.useState(0);
153863
- const [showDetail, setShowDetail] = import_react78.useState(false);
153864
- const scrollboxRef = import_react78.useRef(null);
153865
- const commandsByCategory = import_react78.useMemo(() => {
154473
+ const [selectedIndex, setSelectedIndex] = import_react79.useState(0);
154474
+ const [showDetail, setShowDetail] = import_react79.useState(false);
154475
+ const scrollboxRef = import_react79.useRef(null);
154476
+ const commandsByCategory = import_react79.useMemo(() => {
153866
154477
  const grouped = {};
153867
154478
  for (const cmd of commands2) {
153868
154479
  const category = cmd.category || "Other";
@@ -153873,15 +154484,15 @@ function HelpDialog() {
153873
154484
  }
153874
154485
  return grouped;
153875
154486
  }, [commands2]);
153876
- const flatCommands = import_react78.useMemo(() => {
154487
+ const flatCommands = import_react79.useMemo(() => {
153877
154488
  return commands2;
153878
154489
  }, [commands2]);
153879
- import_react78.useEffect(() => {
154490
+ import_react79.useEffect(() => {
153880
154491
  if (selectedIndex >= flatCommands.length) {
153881
154492
  setSelectedIndex(Math.max(0, flatCommands.length - 1));
153882
154493
  }
153883
154494
  }, [flatCommands.length, selectedIndex]);
153884
- import_react78.useEffect(() => {
154495
+ import_react79.useEffect(() => {
153885
154496
  if (!scrollboxRef.current || flatCommands.length === 0)
153886
154497
  return;
153887
154498
  const scroll = scrollboxRef.current;
@@ -154333,10 +154944,10 @@ function ModelsDisplay() {
154333
154944
  }
154334
154945
 
154335
154946
  // src/tui/context/keybinding.tsx
154336
- var import_react85 = __toESM(require_react(), 1);
154947
+ var import_react86 = __toESM(require_react(), 1);
154337
154948
 
154338
154949
  // src/tui/keybindings/keybind.tsx
154339
- var import_react81 = __toESM(require_react(), 1);
154950
+ var import_react82 = __toESM(require_react(), 1);
154340
154951
 
154341
154952
  // src/tui/keybindings/actions.ts
154342
154953
  var movementActions = [
@@ -154588,7 +155199,7 @@ var allActions = [
154588
155199
  var actionsByKey = new Map(allActions.map((action) => [action.key, action]));
154589
155200
  var actionsById = new Map(allActions.map((action) => [action.id, action]));
154590
155201
  // src/tui/keybindings/keybind.tsx
154591
- var LeaderKeyContext = import_react81.createContext(null);
155202
+ var LeaderKeyContext = import_react82.createContext(null);
154592
155203
  // src/tui/keybindings/registry.ts
154593
155204
  function createKeybindings(deps) {
154594
155205
  const {
@@ -154794,7 +155405,7 @@ var Keybind;
154794
155405
  })(Keybind ||= {});
154795
155406
 
154796
155407
  // src/tui/context/keybinding.tsx
154797
- var KeybindingContext = import_react85.createContext(undefined);
155408
+ var KeybindingContext = import_react86.createContext(undefined);
154798
155409
  function KeybindingProvider({
154799
155410
  children,
154800
155411
  deps
@@ -154835,13 +155446,13 @@ function KeybindingProvider({
154835
155446
  // src/tui/index.tsx
154836
155447
  function App(props) {
154837
155448
  const { appConfig } = props;
154838
- const [focusIndex, setFocusIndex] = import_react87.useState(0);
154839
- const [cwd, setCwd] = import_react87.useState(process.cwd());
154840
- const [ctrlCPressTime, setCtrlCPressTime] = import_react87.useState(null);
154841
- const [showExitWarning, setShowExitWarning] = import_react87.useState(false);
154842
- const [inputKey, setInputKey] = import_react87.useState(0);
154843
- const [showSessionsDialog, setShowSessionsDialog] = import_react87.useState(false);
154844
- const [showShortcutsDialog, setShowShortcutsDialog] = import_react87.useState(false);
155449
+ const [focusIndex, setFocusIndex] = import_react88.useState(0);
155450
+ const [cwd, setCwd] = import_react88.useState(process.cwd());
155451
+ const [ctrlCPressTime, setCtrlCPressTime] = import_react88.useState(null);
155452
+ const [showExitWarning, setShowExitWarning] = import_react88.useState(false);
155453
+ const [inputKey, setInputKey] = import_react88.useState(0);
155454
+ const [showSessionsDialog, setShowSessionsDialog] = import_react88.useState(false);
155455
+ const [showShortcutsDialog, setShowShortcutsDialog] = import_react88.useState(false);
154845
155456
  const navigableItems = ["command-input"];
154846
155457
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ConfigProvider, {
154847
155458
  config: appConfig,
@@ -154915,7 +155526,7 @@ function AppContent({
154915
155526
  path: "providers"
154916
155527
  });
154917
155528
  }
154918
- import_react87.useEffect(() => {
155529
+ import_react88.useEffect(() => {
154919
155530
  if (showExitWarning) {
154920
155531
  const timer = setTimeout(() => {
154921
155532
  setShowExitWarning(false);
@@ -155037,13 +155648,17 @@ function CommandDisplay({
155037
155648
  initialModel: route.data.options?.model
155038
155649
  }, undefined, false, undefined, this)
155039
155650
  }, undefined, false, undefined, this),
155651
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
155652
+ when: "models",
155653
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelsDisplay, {}, undefined, false, undefined, this)
155654
+ }, undefined, false, undefined, this),
155040
155655
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
155041
155656
  when: "providers",
155042
155657
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ProviderManager, {}, undefined, false, undefined, this)
155043
155658
  }, undefined, false, undefined, this),
155044
155659
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
155045
- when: "resume",
155046
- children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ResumeWizard, {}, undefined, false, undefined, this)
155660
+ when: "sessions",
155661
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SessionsBrowser, {}, undefined, false, undefined, this)
155047
155662
  }, undefined, false, undefined, this),
155048
155663
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
155049
155664
  when: "help",
@@ -155060,7 +155675,8 @@ function CommandDisplay({
155060
155675
  if (route.data.type === "session") {
155061
155676
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SessionView, {
155062
155677
  sessionId: route.data.sessionId,
155063
- isResume: route.data.isResume
155678
+ isResume: route.data.isResume,
155679
+ openAsOperator: route.data.openAsOperator
155064
155680
  }, undefined, false, undefined, this);
155065
155681
  }
155066
155682
  return null;