@pensar/apex 0.0.99 → 0.0.100-canary.826dfe3c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/pensar.js CHANGED
@@ -66,6 +66,13 @@ if (command === "benchmark") {
66
66
 
67
67
  // Import and run auth
68
68
  await import(authPath);
69
+ } else if (command === "uninstall") {
70
+ // Run uninstall CLI
71
+ const uninstallPath = join(__dirname, "..", "build", "uninstall.js");
72
+
73
+ process.argv = [process.argv[0], uninstallPath, ...args.slice(1)];
74
+
75
+ await import(uninstallPath);
69
76
  } else if (command === "upgrade" || command === "update") {
70
77
  const currentVersion = getCurrentVersion();
71
78
  console.log(`Current version: v${currentVersion}`);
@@ -89,6 +96,7 @@ if (command === "benchmark") {
89
96
  console.log();
90
97
  console.log("Usage:");
91
98
  console.log(" pensar Launch the TUI (Terminal User Interface)");
99
+ console.log(" pensar uninstall Uninstall Pensar (keeps sessions, memories, skills)");
92
100
  console.log(" pensar upgrade Update pensar to the latest version");
93
101
  console.log(" pensar help Show this help message");
94
102
  console.log(" pensar version Show version number");
@@ -181,6 +189,14 @@ if (command === "benchmark") {
181
189
  console.log(" pensar auth logout Disconnect from Pensar Console");
182
190
  console.log(" pensar auth status Show connection status");
183
191
  console.log();
192
+ console.log("Uninstall Usage:");
193
+ console.log(
194
+ " pensar uninstall Fully uninstall Pensar"
195
+ );
196
+ console.log(
197
+ " pensar uninstall --force Skip confirmation prompt"
198
+ );
199
+ console.log();
184
200
  console.log("Header Modes (for quicktest, pentest, swarm):");
185
201
  console.log(" none No custom headers added to requests");
186
202
  console.log(
package/build/auth.js CHANGED
@@ -8,7 +8,7 @@ import fs from "fs/promises";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "@pensar/apex",
11
- version: "0.0.99",
11
+ version: "0.0.100-canary.826dfe3c",
12
12
  description: "AI-powered penetration testing CLI tool with terminal UI",
13
13
  module: "src/tui/index.tsx",
14
14
  main: "build/index.js",
package/build/index.js CHANGED
@@ -31880,12 +31880,6 @@ var init_openrouter = __esm(() => {
31880
31880
  var PENSAR_MODELS;
31881
31881
  var init_pensar = __esm(() => {
31882
31882
  PENSAR_MODELS = [
31883
- {
31884
- id: "pensar:anthropic.claude-opus-4-6-v1",
31885
- name: "Claude Opus 4.6 (Pensar)",
31886
- provider: "pensar",
31887
- contextLength: 200000
31888
- },
31889
31883
  {
31890
31884
  id: "pensar:anthropic.claude-sonnet-4-5-20250929-v1:0",
31891
31885
  name: "Claude Sonnet 4.5 (Pensar)",
@@ -31977,7 +31971,7 @@ var package_default2;
31977
31971
  var init_package = __esm(() => {
31978
31972
  package_default2 = {
31979
31973
  name: "@pensar/apex",
31980
- version: "0.0.99",
31974
+ version: "0.0.100-canary.826dfe3c",
31981
31975
  description: "AI-powered penetration testing CLI tool with terminal UI",
31982
31976
  module: "src/tui/index.tsx",
31983
31977
  main: "build/index.js",
@@ -193797,6 +193791,19 @@ COMMON SEARCH PATTERNS:
193797
193791
  execute: async ({ query }) => {
193798
193792
  try {
193799
193793
  const cfg = await config2.get();
193794
+ const apiUrl = getPensarApiUrl();
193795
+ const body = JSON.stringify({ query });
193796
+ if (cfg.pensarAPIKey && !cfg.accessToken) {
193797
+ const response2 = await fetch(`${apiUrl}/agents/web_search`, {
193798
+ method: "POST",
193799
+ headers: {
193800
+ "Content-Type": "application/json",
193801
+ "x-api-key": cfg.pensarAPIKey
193802
+ },
193803
+ body
193804
+ });
193805
+ return handleSearchResponse(response2);
193806
+ }
193800
193807
  const tokenResult = await ensureValidToken({
193801
193808
  accessToken: cfg.accessToken,
193802
193809
  refreshToken: cfg.refreshToken,
@@ -193823,8 +193830,6 @@ COMMON SEARCH PATTERNS:
193823
193830
  error: "Web search requires authentication. Please sign in again to your Pensar account."
193824
193831
  };
193825
193832
  }
193826
- const apiUrl = getPensarApiUrl();
193827
- const body = JSON.stringify({ query });
193828
193833
  const { signature, timestamp, nonce } = signGatewayRequest(cfg.gatewaySigningKey, "web_search", body);
193829
193834
  const response = await fetch(`${apiUrl}/agents/web_search`, {
193830
193835
  method: "POST",
@@ -193838,40 +193843,7 @@ COMMON SEARCH PATTERNS:
193838
193843
  },
193839
193844
  body
193840
193845
  });
193841
- if (!response.ok) {
193842
- if (response.status === 401) {
193843
- return {
193844
- success: false,
193845
- results: [],
193846
- error: "Authentication failed. Please sign in again to your Pensar account."
193847
- };
193848
- }
193849
- if (response.status === 429) {
193850
- return {
193851
- success: false,
193852
- results: [],
193853
- error: "Rate limit exceeded. Please wait a moment before searching again."
193854
- };
193855
- }
193856
- const errorText = await response.text().catch(() => "Unknown error");
193857
- return {
193858
- success: false,
193859
- results: [],
193860
- error: `Web search failed: ${response.status} ${response.statusText}. ${errorText}`
193861
- };
193862
- }
193863
- const data = await response.json();
193864
- if (data.error) {
193865
- return {
193866
- success: false,
193867
- results: [],
193868
- error: data.error
193869
- };
193870
- }
193871
- return {
193872
- success: true,
193873
- results: data.results || []
193874
- };
193846
+ return handleSearchResponse(response);
193875
193847
  } catch (error40) {
193876
193848
  const errorMsg = error40 instanceof Error ? error40.message : String(error40);
193877
193849
  return {
@@ -193883,6 +193855,42 @@ COMMON SEARCH PATTERNS:
193883
193855
  }
193884
193856
  });
193885
193857
  }
193858
+ async function handleSearchResponse(response) {
193859
+ if (!response.ok) {
193860
+ if (response.status === 401) {
193861
+ return {
193862
+ success: false,
193863
+ results: [],
193864
+ error: "Authentication failed. Please sign in again to your Pensar account."
193865
+ };
193866
+ }
193867
+ if (response.status === 429) {
193868
+ return {
193869
+ success: false,
193870
+ results: [],
193871
+ error: "Rate limit exceeded. Please wait a moment before searching again."
193872
+ };
193873
+ }
193874
+ const errorText = await response.text().catch(() => "Unknown error");
193875
+ return {
193876
+ success: false,
193877
+ results: [],
193878
+ error: `Web search failed: ${response.status} ${response.statusText}. ${errorText}`
193879
+ };
193880
+ }
193881
+ const data = await response.json();
193882
+ if (data.error) {
193883
+ return {
193884
+ success: false,
193885
+ results: [],
193886
+ error: data.error
193887
+ };
193888
+ }
193889
+ return {
193890
+ success: true,
193891
+ results: data.results || []
193892
+ };
193893
+ }
193886
193894
  var webSearchInputSchema2;
193887
193895
  var init_webSearch = __esm(() => {
193888
193896
  init_dist5();
@@ -194042,7 +194050,7 @@ function createAllTools(ctx4) {
194042
194050
  get_page: getPage(ctx4)
194043
194051
  };
194044
194052
  }
194045
- var ALL_TOOL_NAMES;
194053
+ var ALL_TOOL_NAMES, PLAN_MODE_TOOL_NAMES;
194046
194054
  var init_tools = __esm(() => {
194047
194055
  init_browserTools();
194048
194056
  init_sandboxPlaywright();
@@ -194151,6 +194159,40 @@ var init_tools = __esm(() => {
194151
194159
  "web_search",
194152
194160
  "get_page"
194153
194161
  ];
194162
+ PLAN_MODE_TOOL_NAMES = [
194163
+ "browser_navigate",
194164
+ "browser_snapshot",
194165
+ "browser_screenshot",
194166
+ "browser_click",
194167
+ "browser_fill",
194168
+ "browser_evaluate",
194169
+ "browser_console",
194170
+ "browser_get_cookies",
194171
+ "execute_command",
194172
+ "http_request",
194173
+ "read_file",
194174
+ "list_files",
194175
+ "grep",
194176
+ "authenticate_session",
194177
+ "delegate_to_auth_subagent",
194178
+ "create_attack_surface_report",
194179
+ "complete_authentication",
194180
+ "run_attack_surface",
194181
+ "spawn_pentest_swarm",
194182
+ "spawn_coding_agent",
194183
+ "provide_comparison_results",
194184
+ "add_memory",
194185
+ "list_memories",
194186
+ "get_memory",
194187
+ "email_list_inboxes",
194188
+ "email_list_messages",
194189
+ "email_get_message",
194190
+ "email_search_messages",
194191
+ "email_get_attachments",
194192
+ "email_mark_read",
194193
+ "web_search",
194194
+ "get_page"
194195
+ ];
194154
194196
  });
194155
194197
 
194156
194198
  // src/core/agents/offSecAgent/tools/response.ts
@@ -194892,7 +194934,11 @@ var init_offensiveSecurityAgent = __esm(() => {
194892
194934
  }
194893
194935
  const hasEmail = (input.session.config?.emailIntegration?.inboxes?.length ?? 0) > 0;
194894
194936
  const emailToolSet = new Set(EMAIL_TOOL_NAMES);
194895
- const activeTools = hasEmail ? input.activeTools : input.activeTools.filter((t3) => !emailToolSet.has(t3));
194937
+ let activeTools = hasEmail ? input.activeTools : input.activeTools.filter((t3) => !emailToolSet.has(t3));
194938
+ if (input.mode === "plan") {
194939
+ const planSet = new Set(PLAN_MODE_TOOL_NAMES);
194940
+ activeTools = activeTools.filter((t3) => planSet.has(t3));
194941
+ }
194896
194942
  const messagesDir = input.messagesDir ?? input.session.rootPath;
194897
194943
  if (!existsSync21(messagesDir)) {
194898
194944
  mkdirSync10(messagesDir, { recursive: true });
@@ -276318,6 +276364,37 @@ var providerOrder = [
276318
276364
  "bedrock",
276319
276365
  "local"
276320
276366
  ];
276367
+ function setsAreEqual(a, b2) {
276368
+ return a.size === b2.size && [...a].every((value) => b2.has(value));
276369
+ }
276370
+ function PickerRow({
276371
+ children,
276372
+ id,
276373
+ paddingLeft,
276374
+ paddingRight,
276375
+ backgroundColor,
276376
+ flexDirection = "row",
276377
+ gap = 0
276378
+ }) {
276379
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
276380
+ id,
276381
+ width: "100%",
276382
+ overflow: "hidden",
276383
+ flexDirection,
276384
+ paddingLeft,
276385
+ paddingRight,
276386
+ backgroundColor,
276387
+ gap,
276388
+ children
276389
+ }, undefined, false, undefined, this);
276390
+ }
276391
+ function getNavItemId(item) {
276392
+ if (item.type === "provider")
276393
+ return `provider-${item.provider}`;
276394
+ if (item.type === "model")
276395
+ return `model-${item.model.id}`;
276396
+ return `local-input-${item.field}`;
276397
+ }
276321
276398
  function ModelPicker({
276322
276399
  config: config3,
276323
276400
  selectedModel,
@@ -276328,6 +276405,7 @@ function ModelPicker({
276328
276405
  isModelUserSelected = false
276329
276406
  }) {
276330
276407
  const { colors: colors2 } = useTheme();
276408
+ const scrollBoxRef = import_react34.useRef(null);
276331
276409
  const [availableModels, setAvailableModels] = import_react34.useState([]);
276332
276410
  const [searchQuery, setSearchQuery] = import_react34.useState("");
276333
276411
  const [expandedProviders, setExpandedProviders] = import_react34.useState(new Set(["anthropic"]));
@@ -276340,17 +276418,29 @@ function ModelPicker({
276340
276418
  setLocalModelName(config3?.localModelName ?? "");
276341
276419
  }, [config3?.localModelUrl, config3?.localModelName]);
276342
276420
  import_react34.useEffect(() => {
276343
- if (config3) {
276344
- const models = getAvailableModels(config3);
276345
- setAvailableModels(models);
276346
- if (models.length > 0) {
276347
- const currentModel = models.find((m2) => m2.id === selectedModel.id) || models[0];
276348
- if (currentModel) {
276349
- setExpandedProviders(new Set([currentModel.provider]));
276350
- }
276351
- }
276421
+ if (!config3) {
276422
+ setAvailableModels([]);
276423
+ return;
276352
276424
  }
276353
- }, [config3, selectedModel.id]);
276425
+ setAvailableModels(getAvailableModels(config3));
276426
+ }, [config3]);
276427
+ import_react34.useEffect(() => {
276428
+ const availableProviders = new Set(availableModels.map((model) => model.provider));
276429
+ setExpandedProviders((prev) => {
276430
+ if (availableProviders.size === 0) {
276431
+ return prev.size === 0 ? prev : new Set;
276432
+ }
276433
+ const preservedProviders = new Set([...prev].filter((provider) => availableProviders.has(provider)));
276434
+ if (preservedProviders.size > 0) {
276435
+ return setsAreEqual(prev, preservedProviders) ? prev : preservedProviders;
276436
+ }
276437
+ const fallbackProvider = availableProviders.has(selectedModel.provider) ? selectedModel.provider : availableModels[0]?.provider;
276438
+ if (!fallbackProvider) {
276439
+ return prev.size === 0 ? prev : new Set;
276440
+ }
276441
+ return prev.size === 1 && prev.has(fallbackProvider) ? prev : new Set([fallbackProvider]);
276442
+ });
276443
+ }, [availableModels, selectedModel.provider]);
276354
276444
  const groupedModels = import_react34.useMemo(() => {
276355
276445
  const groups = {};
276356
276446
  const query = searchQuery.toLowerCase().trim();
@@ -276403,6 +276493,12 @@ function ModelPicker({
276403
276493
  import_react34.useEffect(() => {
276404
276494
  setFocusedIndex((prev) => Math.min(prev, Math.max(0, navigationItems.length - 1)));
276405
276495
  }, [navigationItems.length]);
276496
+ import_react34.useEffect(() => {
276497
+ const item = navigationItems[focusedIndex];
276498
+ if (!item)
276499
+ return;
276500
+ scrollToChild(scrollBoxRef.current, getNavItemId(item));
276501
+ }, [focusedIndex, navigationItems]);
276406
276502
  const commitLocalConfig = import_react34.useCallback((url2, modelName) => {
276407
276503
  if (!onConfigUpdate)
276408
276504
  return;
@@ -276510,169 +276606,206 @@ function ModelPicker({
276510
276606
  useKeyboard((key) => {
276511
276607
  handleKeyboard(key);
276512
276608
  });
276609
+ const isProviderFocused = (provider) => navigationItems[focusedIndex]?.type === "provider" && navigationItems[focusedIndex].provider === provider;
276610
+ const isModelFocused = (modelId) => navigationItems[focusedIndex]?.type === "model" && navigationItems[focusedIndex].model.id === modelId;
276611
+ const isLocalFieldFocused = (field) => navigationItems[focusedIndex]?.type === "local-input" && navigationItems[focusedIndex].field === field;
276513
276612
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
276514
276613
  flexDirection: "column",
276515
276614
  gap: 0,
276615
+ width: "100%",
276616
+ flexGrow: 1,
276617
+ flexShrink: 1,
276618
+ overflow: "hidden",
276516
276619
  children: [
276517
- searchQuery ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276518
- fg: colors2.text,
276519
- children: [
276520
- "Search: ",
276521
- searchQuery,
276522
- "_"
276523
- ]
276524
- }, undefined, true, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276525
- fg: colors2.textMuted,
276526
- children: "Type to search models..."
276620
+ searchQuery ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276621
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276622
+ fg: colors2.text,
276623
+ children: [
276624
+ "Search: ",
276625
+ searchQuery
276626
+ ]
276627
+ }, undefined, true, undefined, this)
276628
+ }, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276629
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276630
+ fg: colors2.textMuted,
276631
+ children: "Type to search models..."
276632
+ }, undefined, false, undefined, this)
276527
276633
  }, undefined, false, undefined, this),
276528
- providerOrder.map((provider) => {
276529
- const isExpanded = expandedProviders.has(provider);
276530
- const providerName = providerNames[provider] || provider;
276531
- const isProviderFocused = navigationItems[focusedIndex]?.type === "provider" && navigationItems[focusedIndex].provider === provider;
276532
- if (provider === "local") {
276533
- const localModels = groupedModels["local"];
276534
- const modelCount = localModels?.length ?? 0;
276535
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
276536
- flexDirection: "column",
276537
- gap: 0,
276538
- children: [
276539
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276540
- fg: isProviderFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
276634
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("scrollbox", {
276635
+ ref: scrollBoxRef,
276636
+ style: {
276637
+ rootOptions: {
276638
+ flexGrow: 1,
276639
+ flexShrink: 1,
276640
+ width: "100%",
276641
+ overflow: "hidden"
276642
+ },
276643
+ contentOptions: {
276644
+ flexDirection: "column"
276645
+ },
276646
+ scrollbarOptions: {
276647
+ visible: false
276648
+ }
276649
+ },
276650
+ stickyScroll: false,
276651
+ children: providerOrder.flatMap((provider) => {
276652
+ const isExpanded = expandedProviders.has(provider);
276653
+ const providerName = providerNames[provider] || provider;
276654
+ const isFocused = isProviderFocused(provider);
276655
+ if (provider === "local") {
276656
+ const localModels = groupedModels["local"];
276657
+ const modelCount = localModels?.length ?? 0;
276658
+ const elements2 = [];
276659
+ elements2.push(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276660
+ id: "provider-local",
276661
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276662
+ fg: isFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
276541
276663
  children: [
276542
- isProviderFocused ? "❯" : isExpanded ? "▾" : "▸",
276664
+ isFocused ? "❯" : isExpanded ? "▾" : "▸",
276543
276665
  " ",
276544
276666
  providerName,
276545
276667
  modelCount > 0 ? ` (${modelCount})` : ""
276546
276668
  ]
276547
- }, undefined, true, undefined, this),
276548
- isExpanded && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
276549
- flexDirection: "column",
276550
- gap: 0,
276551
- paddingLeft: 2,
276552
- children: [
276553
- (() => {
276554
- const isUrlFocused = navigationItems[focusedIndex]?.type === "local-input" && navigationItems[focusedIndex].field === "url";
276555
- const isUrlEditing = editingLocalField === "url";
276556
- if (isUrlEditing) {
276557
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
276558
- flexDirection: "row",
276559
- children: [
276560
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276561
- fg: colors2.primary,
276562
- children: " URL: "
276563
- }, undefined, false, undefined, this),
276564
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
276565
- focused: true,
276566
- value: localUrl,
276567
- backgroundColor: "transparent",
276568
- cursorColor: colors2.textMuted,
276569
- onInput: (v2) => setLocalUrl(typeof v2 === "string" ? v2 : ""),
276570
- onPaste: (event) => {
276571
- const cleaned = String(event.text).replace(/\r?\n/g, "");
276572
- setLocalUrl((prev) => `${prev}${cleaned}`);
276573
- },
276574
- onSubmit: finishEditing
276575
- }, undefined, false, undefined, this)
276576
- ]
276577
- }, undefined, true, undefined, this);
276578
- }
276579
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276580
- fg: isUrlFocused ? colors2.primary : colors2.textMuted,
276581
- children: ` URL: ${localUrl || "(press Enter to set)"}`
276582
- }, undefined, false, undefined, this);
276583
- })(),
276584
- (() => {
276585
- const isModelFocused = navigationItems[focusedIndex]?.type === "local-input" && navigationItems[focusedIndex].field === "model";
276586
- const isModelEditing = editingLocalField === "model";
276587
- if (isModelEditing) {
276588
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
276589
- flexDirection: "row",
276590
- children: [
276591
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276592
- fg: colors2.primary,
276593
- children: " Model: "
276594
- }, undefined, false, undefined, this),
276595
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
276596
- focused: true,
276597
- value: localModelName,
276598
- backgroundColor: "transparent",
276599
- cursorColor: colors2.textMuted,
276600
- onInput: (v2) => setLocalModelName(typeof v2 === "string" ? v2 : ""),
276601
- onPaste: (event) => {
276602
- const cleaned = String(event.text).replace(/\r?\n/g, "");
276603
- setLocalModelName((prev) => `${prev}${cleaned}`);
276604
- },
276605
- onSubmit: finishEditing
276606
- }, undefined, false, undefined, this)
276607
- ]
276608
- }, undefined, true, undefined, this);
276609
- }
276610
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276611
- fg: isModelFocused ? colors2.primary : colors2.textMuted,
276612
- children: ` Model: ${localModelName || "(press Enter to set)"}`
276613
- }, undefined, false, undefined, this);
276614
- })(),
276615
- localModels?.map((m2) => {
276616
- const isSelected = m2.id === selectedModel.id;
276617
- const isFocused = navigationItems[focusedIndex]?.type === "model" && navigationItems[focusedIndex].model.id === m2.id;
276618
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276619
- fg: isFocused ? colors2.primary : colors2.textMuted,
276669
+ }, undefined, true, undefined, this)
276670
+ }, "local", false, undefined, this));
276671
+ if (isExpanded) {
276672
+ const isUrlFocused = isLocalFieldFocused("url");
276673
+ const isUrlEditing = editingLocalField === "url";
276674
+ if (isUrlEditing) {
276675
+ elements2.push(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276676
+ id: "local-input-url",
276677
+ paddingLeft: 2,
276678
+ children: [
276679
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276680
+ fg: colors2.primary,
276681
+ children: " URL: "
276682
+ }, undefined, false, undefined, this),
276683
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
276684
+ focused: true,
276685
+ value: localUrl,
276686
+ backgroundColor: "transparent",
276687
+ cursorColor: colors2.textMuted,
276688
+ onInput: (v2) => setLocalUrl(typeof v2 === "string" ? v2 : ""),
276689
+ onPaste: (event) => {
276690
+ const cleaned = String(event.text).replace(/\r?\n/g, "");
276691
+ setLocalUrl((prev) => `${prev}${cleaned}`);
276692
+ },
276693
+ onSubmit: finishEditing
276694
+ }, undefined, false, undefined, this)
276695
+ ]
276696
+ }, "local-url", true, undefined, this));
276697
+ } else {
276698
+ elements2.push(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276699
+ id: "local-input-url",
276700
+ paddingLeft: 2,
276701
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276702
+ fg: isUrlFocused ? colors2.primary : colors2.textMuted,
276703
+ children: ` URL: ${localUrl || "(press Enter to set)"}`
276704
+ }, undefined, false, undefined, this)
276705
+ }, "local-url", false, undefined, this));
276706
+ }
276707
+ const isModelFieldFocused = isLocalFieldFocused("model");
276708
+ const isModelEditing = editingLocalField === "model";
276709
+ if (isModelEditing) {
276710
+ elements2.push(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276711
+ id: "local-input-model",
276712
+ paddingLeft: 2,
276713
+ children: [
276714
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276715
+ fg: colors2.primary,
276716
+ children: " Model: "
276717
+ }, undefined, false, undefined, this),
276718
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
276719
+ focused: true,
276720
+ value: localModelName,
276721
+ backgroundColor: "transparent",
276722
+ cursorColor: colors2.textMuted,
276723
+ onInput: (v2) => setLocalModelName(typeof v2 === "string" ? v2 : ""),
276724
+ onPaste: (event) => {
276725
+ const cleaned = String(event.text).replace(/\r?\n/g, "");
276726
+ setLocalModelName((prev) => `${prev}${cleaned}`);
276727
+ },
276728
+ onSubmit: finishEditing
276729
+ }, undefined, false, undefined, this)
276730
+ ]
276731
+ }, "local-model", true, undefined, this));
276732
+ } else {
276733
+ elements2.push(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276734
+ id: "local-input-model",
276735
+ paddingLeft: 2,
276736
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276737
+ fg: isModelFieldFocused ? colors2.primary : colors2.textMuted,
276738
+ children: ` Model: ${localModelName || "(press Enter to set)"}`
276739
+ }, undefined, false, undefined, this)
276740
+ }, "local-model", false, undefined, this));
276741
+ }
276742
+ if (localModels) {
276743
+ for (const m2 of localModels) {
276744
+ const isSelected = m2.id === selectedModel.id;
276745
+ const isMFocused = isModelFocused(m2.id);
276746
+ elements2.push(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276747
+ id: `model-${m2.id}`,
276748
+ paddingLeft: 2,
276749
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276750
+ fg: isMFocused ? colors2.primary : colors2.textMuted,
276620
276751
  children: [
276621
276752
  isSelected ? "●" : "○",
276622
276753
  " ",
276623
276754
  m2.name
276624
276755
  ]
276625
- }, m2.id, true, undefined, this);
276626
- })
276627
- ]
276628
- }, undefined, true, undefined, this)
276629
- ]
276630
- }, "local", true, undefined, this);
276631
- }
276632
- const models = groupedModels[provider];
276633
- if (!models || models.length === 0)
276634
- return null;
276635
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
276636
- flexDirection: "column",
276637
- gap: 0,
276638
- children: [
276639
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276640
- fg: isProviderFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
276756
+ }, undefined, true, undefined, this)
276757
+ }, m2.id, false, undefined, this));
276758
+ }
276759
+ }
276760
+ }
276761
+ return elements2;
276762
+ }
276763
+ const models = groupedModels[provider];
276764
+ if (!models || models.length === 0)
276765
+ return [];
276766
+ const elements = [];
276767
+ elements.push(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276768
+ id: `provider-${provider}`,
276769
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276770
+ fg: isFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
276641
276771
  children: [
276642
- isProviderFocused ? "❯" : isExpanded ? "▾" : "▸",
276772
+ isFocused ? "❯" : isExpanded ? "▾" : "▸",
276643
276773
  " ",
276644
276774
  providerName,
276645
- " ",
276646
- "(",
276775
+ " (",
276647
276776
  models.length,
276648
276777
  ")"
276649
276778
  ]
276650
- }, undefined, true, undefined, this),
276651
- isExpanded && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
276652
- flexDirection: "column",
276653
- gap: 0,
276654
- paddingLeft: 2,
276655
- children: models.map((m2) => {
276656
- const isSelected = m2.id === selectedModel.id;
276657
- const isFocused = navigationItems[focusedIndex]?.type === "model" && navigationItems[focusedIndex].model.id === m2.id;
276658
- const isDefault = m2.id === "claude-haiku-4-5" || m2.id === "gpt-4o-mini";
276659
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276660
- fg: isFocused ? colors2.primary : colors2.textMuted,
276779
+ }, undefined, true, undefined, this)
276780
+ }, provider, false, undefined, this));
276781
+ if (isExpanded) {
276782
+ for (const m2 of models) {
276783
+ const isSelected = m2.id === selectedModel.id;
276784
+ const isMFocused = isModelFocused(m2.id);
276785
+ const isDefault = m2.id === "claude-haiku-4-5" || m2.id === "gpt-4o-mini";
276786
+ elements.push(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276787
+ id: `model-${m2.id}`,
276788
+ paddingLeft: 2,
276789
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276790
+ fg: isMFocused ? colors2.primary : colors2.textMuted,
276661
276791
  children: [
276662
276792
  isSelected ? "●" : "○",
276663
276793
  " ",
276664
276794
  m2.name,
276665
276795
  isDefault && !isModelUserSelected && isSelected ? " [default]" : ""
276666
276796
  ]
276667
- }, m2.id, true, undefined, this);
276668
- })
276669
- }, undefined, false, undefined, this)
276670
- ]
276671
- }, provider, true, undefined, this);
276672
- }),
276673
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276674
- fg: colors2.textMuted,
276675
- children: editingLocalField ? "Type or paste | Enter/Esc to confirm" : "↑/↓ navigate | ←/→ collapse/expand | Type to search"
276797
+ }, undefined, true, undefined, this)
276798
+ }, m2.id, false, undefined, this));
276799
+ }
276800
+ }
276801
+ return elements;
276802
+ })
276803
+ }, undefined, false, undefined, this),
276804
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PickerRow, {
276805
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
276806
+ fg: colors2.textMuted,
276807
+ children: editingLocalField ? "Type or paste | Enter/Esc to confirm" : "↑/↓ navigate | ←/→ collapse/expand | Type to search"
276808
+ }, undefined, false, undefined, this)
276676
276809
  }, undefined, false, undefined, this)
276677
276810
  ]
276678
276811
  }, undefined, true, undefined, this);
@@ -276847,7 +276980,8 @@ function ConfigView({ config: config3, onBack, onStart }) {
276847
276980
  borderColor: focusedField === "model" ? colors2.primary : colors2.border,
276848
276981
  paddingLeft: 1,
276849
276982
  paddingRight: 1,
276850
- maxHeight: 8,
276983
+ height: 8,
276984
+ overflow: "hidden",
276851
276985
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelPicker, {
276852
276986
  config: config3,
276853
276987
  selectedModel,
@@ -280254,6 +280388,13 @@ function HelpDialog() {
280254
280388
  }, undefined, false, undefined, this);
280255
280389
  }
280256
280390
  // src/tui/components/commands/models-display.tsx
280391
+ function ClippedLine({ children }) {
280392
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
280393
+ width: "100%",
280394
+ overflow: "hidden",
280395
+ children
280396
+ }, undefined, false, undefined, this);
280397
+ }
280257
280398
  function ModelsDisplay() {
280258
280399
  const { colors: colors2 } = useTheme();
280259
280400
  const route = useRoute();
@@ -280281,42 +280422,52 @@ function ModelsDisplay() {
280281
280422
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
280282
280423
  flexDirection: "column",
280283
280424
  width: "100%",
280425
+ height: "100%",
280284
280426
  paddingLeft: 4,
280427
+ paddingRight: 4,
280285
280428
  paddingTop: 2,
280429
+ overflow: "hidden",
280286
280430
  children: [
280287
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
280288
- children: [
280289
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280290
- fg: colors2.primary,
280291
- children: "█ "
280292
- }, undefined, false, undefined, this),
280293
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280294
- fg: colors2.text,
280295
- children: "Select AI Model"
280296
- }, undefined, false, undefined, this),
280297
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280298
- fg: colors2.textMuted,
280299
- children: [
280300
- " (",
280301
- model.name,
280302
- ")"
280303
- ]
280304
- }, undefined, true, undefined, this),
280305
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280306
- fg: colors2.textMuted,
280307
- children: [
280308
- " ",
280309
- "[",
280310
- isModelUserSelected ? "user" : "default",
280311
- "]"
280312
- ]
280313
- }, undefined, true, undefined, this)
280314
- ]
280315
- }, undefined, true, undefined, this),
280431
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ClippedLine, {
280432
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
280433
+ children: [
280434
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280435
+ fg: colors2.primary,
280436
+ children: "█ "
280437
+ }, undefined, false, undefined, this),
280438
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280439
+ fg: colors2.text,
280440
+ children: "Select AI Model"
280441
+ }, undefined, false, undefined, this),
280442
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280443
+ fg: colors2.textMuted,
280444
+ children: [
280445
+ " (",
280446
+ model.name,
280447
+ ")"
280448
+ ]
280449
+ }, undefined, true, undefined, this),
280450
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280451
+ fg: colors2.textMuted,
280452
+ children: [
280453
+ " ",
280454
+ "[",
280455
+ isModelUserSelected ? "user" : "default",
280456
+ "]"
280457
+ ]
280458
+ }, undefined, true, undefined, this)
280459
+ ]
280460
+ }, undefined, true, undefined, this)
280461
+ }, undefined, false, undefined, this),
280316
280462
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
280317
280463
  flexDirection: "column",
280464
+ width: "100%",
280318
280465
  paddingLeft: 2,
280466
+ paddingRight: 2,
280319
280467
  marginTop: 1,
280468
+ flexGrow: 1,
280469
+ flexShrink: 1,
280470
+ overflow: "hidden",
280320
280471
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelPicker, {
280321
280472
  config: config3.data,
280322
280473
  selectedModel: model,
@@ -280331,66 +280482,72 @@ function ModelsDisplay() {
280331
280482
  flexDirection: "column",
280332
280483
  marginTop: 2,
280333
280484
  children: [
280334
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
280335
- children: [
280336
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280337
- fg: colors2.primary,
280338
- children: "█ "
280339
- }, undefined, false, undefined, this),
280340
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280341
- fg: colors2.textMuted,
280342
- children: "Press "
280343
- }, undefined, false, undefined, this),
280344
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280345
- fg: colors2.text,
280346
- children: "[Enter]"
280347
- }, undefined, false, undefined, this),
280348
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280349
- fg: colors2.textMuted,
280350
- children: " to confirm"
280351
- }, undefined, false, undefined, this)
280352
- ]
280353
- }, undefined, true, undefined, this),
280354
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
280355
- children: [
280356
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280357
- fg: colors2.primary,
280358
- children: "█ "
280359
- }, undefined, false, undefined, this),
280360
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280361
- fg: colors2.textMuted,
280362
- children: "Press "
280363
- }, undefined, false, undefined, this),
280364
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280365
- fg: colors2.text,
280366
- children: "[ESC]"
280367
- }, undefined, false, undefined, this),
280368
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280369
- fg: colors2.textMuted,
280370
- children: " to go back"
280371
- }, undefined, false, undefined, this)
280372
- ]
280373
- }, undefined, true, undefined, this),
280374
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
280375
- children: [
280376
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280377
- fg: colors2.primary,
280378
- children: "█ "
280379
- }, undefined, false, undefined, this),
280380
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280381
- fg: colors2.textMuted,
280382
- children: "Press "
280383
- }, undefined, false, undefined, this),
280384
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280385
- fg: colors2.text,
280386
- children: "[Ctrl+P]"
280387
- }, undefined, false, undefined, this),
280388
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280389
- fg: colors2.textMuted,
280390
- children: " to connect provider"
280391
- }, undefined, false, undefined, this)
280392
- ]
280393
- }, undefined, true, undefined, this)
280485
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ClippedLine, {
280486
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
280487
+ children: [
280488
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280489
+ fg: colors2.primary,
280490
+ children: "█ "
280491
+ }, undefined, false, undefined, this),
280492
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280493
+ fg: colors2.textMuted,
280494
+ children: "Press "
280495
+ }, undefined, false, undefined, this),
280496
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280497
+ fg: colors2.text,
280498
+ children: "[Enter]"
280499
+ }, undefined, false, undefined, this),
280500
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280501
+ fg: colors2.textMuted,
280502
+ children: " to confirm"
280503
+ }, undefined, false, undefined, this)
280504
+ ]
280505
+ }, undefined, true, undefined, this)
280506
+ }, undefined, false, undefined, this),
280507
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ClippedLine, {
280508
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
280509
+ children: [
280510
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280511
+ fg: colors2.primary,
280512
+ children: "█ "
280513
+ }, undefined, false, undefined, this),
280514
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280515
+ fg: colors2.textMuted,
280516
+ children: "Press "
280517
+ }, undefined, false, undefined, this),
280518
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280519
+ fg: colors2.text,
280520
+ children: "[ESC]"
280521
+ }, undefined, false, undefined, this),
280522
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280523
+ fg: colors2.textMuted,
280524
+ children: " to go back"
280525
+ }, undefined, false, undefined, this)
280526
+ ]
280527
+ }, undefined, true, undefined, this)
280528
+ }, undefined, false, undefined, this),
280529
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ClippedLine, {
280530
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
280531
+ children: [
280532
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280533
+ fg: colors2.primary,
280534
+ children: "█ "
280535
+ }, undefined, false, undefined, this),
280536
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280537
+ fg: colors2.textMuted,
280538
+ children: "Press "
280539
+ }, undefined, false, undefined, this),
280540
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280541
+ fg: colors2.text,
280542
+ children: "[Ctrl+P]"
280543
+ }, undefined, false, undefined, this),
280544
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
280545
+ fg: colors2.textMuted,
280546
+ children: " to connect provider"
280547
+ }, undefined, false, undefined, this)
280548
+ ]
280549
+ }, undefined, true, undefined, this)
280550
+ }, undefined, false, undefined, this)
280394
280551
  ]
280395
280552
  }, undefined, true, undefined, this)
280396
280553
  ]
@@ -286458,8 +286615,10 @@ function resolveKeyboardShortcut(key, status, inputValue, hasPendingApprovals, d
286458
286615
  return { type: "toggle-verbose" };
286459
286616
  if (key.ctrl && key.name === "l")
286460
286617
  return { type: "toggle-expanded-logs" };
286461
- if (key.name === "tab" && key.shift)
286618
+ if (key.name === "tab" && key.shift && key.meta)
286462
286619
  return { type: "toggle-approval" };
286620
+ if (key.name === "tab" && key.shift)
286621
+ return { type: "toggle-mode" };
286463
286622
  if (status === "waiting" && hasPendingApprovals && (key.name === "y" || key.raw === "Y")) {
286464
286623
  return { type: "approve" };
286465
286624
  }
@@ -286474,7 +286633,9 @@ function resolveAbortAction(commandCancelled, cancelCommand) {
286474
286633
  }
286475
286634
  return { type: "kill-agent" };
286476
286635
  }
286477
- function buildOperatorSystemPrompt(target, operatorState) {
286636
+ function buildOperatorSystemPrompt(target, operatorState, agentMode) {
286637
+ const modeNote = agentMode === "plan" ? `
286638
+ Agent mode: PLAN — read-only tools only, no mutations allowed` : "";
286478
286639
  return `${BASE_SYSTEM_PROMPT}
286479
286640
 
286480
286641
  # Operator Mode
@@ -286483,7 +286644,7 @@ You are operating in interactive operator mode. The human operator will guide yo
286483
286644
 
286484
286645
  Target: ${target || "unknown"}
286485
286646
  Stage: ${operatorState.currentStage}
286486
- Command approval: ${operatorState.requireApproval ? "enabled — the operator will approve each tool call" : "disabled — tool calls execute automatically"}`;
286647
+ Command approval: ${operatorState.requireApproval ? "enabled — the operator will approve each tool call" : "disabled — tool calls execute automatically"}${modeNote}`;
286487
286648
  }
286488
286649
  function resolveInputFocused(status, dialogStackLength, externalDialogOpen) {
286489
286650
  return status !== "running" && dialogStackLength === 0 && !externalDialogOpen;
@@ -286653,6 +286814,7 @@ function OperatorDashboard({
286653
286814
  const [lastApprovedAction, setLastApprovedAction] = import_react79.useState(null);
286654
286815
  const [verboseMode, setVerboseMode] = import_react79.useState(false);
286655
286816
  const [expandedLogs, setExpandedLogs] = import_react79.useState(false);
286817
+ const [agentMode, setAgentMode] = import_react79.useState("default");
286656
286818
  const tokenUsageRef = import_react79.useRef(tokenUsage);
286657
286819
  import_react79.useEffect(() => {
286658
286820
  tokenUsageRef.current = tokenUsage;
@@ -287095,7 +287257,10 @@ function OperatorDashboard({
287095
287257
  messages: nextMessages,
287096
287258
  stopWhen: [stepCountIs(1e4)],
287097
287259
  target: initialConfig?.target,
287098
- activeTools: [...ALL_TOOL_NAMES],
287260
+ activeTools: [
287261
+ ...agentMode === "plan" ? PLAN_MODE_TOOL_NAMES : ALL_TOOL_NAMES
287262
+ ],
287263
+ mode: agentMode,
287099
287264
  abortSignal: controller.signal,
287100
287265
  authConfig: buildAuthConfig(config3.data),
287101
287266
  approvalGate: approvalGateRef.current,
@@ -287111,7 +287276,7 @@ function OperatorDashboard({
287111
287276
  if (session) {
287112
287277
  agentResult = await runOffensiveSecurityAgent({
287113
287278
  ...commonInput,
287114
- system: buildOperatorSystemPrompt(initialConfig?.target, operatorState),
287279
+ system: buildOperatorSystemPrompt(initialConfig?.target, operatorState, agentMode),
287115
287280
  session
287116
287281
  });
287117
287282
  } else {
@@ -287127,7 +287292,7 @@ function OperatorDashboard({
287127
287292
  };
287128
287293
  agentResult = await runOffensiveSecurityAgent({
287129
287294
  ...commonInput,
287130
- system: buildOperatorSystemPrompt(initialConfig?.target, operatorState),
287295
+ system: buildOperatorSystemPrompt(initialConfig?.target, operatorState, agentMode),
287131
287296
  sessionConfig,
287132
287297
  onNameGenerated: (name26) => {
287133
287298
  pendingNameRef.current = name26;
@@ -287192,6 +287357,7 @@ function OperatorDashboard({
287192
287357
  model.id,
287193
287358
  config3.data,
287194
287359
  operatorState,
287360
+ agentMode,
287195
287361
  addTokenUsage,
287196
287362
  appendText,
287197
287363
  addStreamingToolCall,
@@ -287382,6 +287548,9 @@ function OperatorDashboard({
287382
287548
  return { ...prev, requireApproval: newVal };
287383
287549
  });
287384
287550
  }, []);
287551
+ const toggleMode = import_react79.useCallback(() => {
287552
+ setAgentMode((prev) => prev === "default" ? "plan" : "default");
287553
+ }, []);
287385
287554
  useKeyboard((key) => {
287386
287555
  if (status === "running" && queuedMessages.length > 0 && !inputValue.trim()) {
287387
287556
  if (key.name === "up") {
@@ -287453,6 +287622,9 @@ function OperatorDashboard({
287453
287622
  case "toggle-approval":
287454
287623
  toggleApproval();
287455
287624
  return;
287625
+ case "toggle-mode":
287626
+ toggleMode();
287627
+ return;
287456
287628
  case "approve":
287457
287629
  handleApprove();
287458
287630
  return;
@@ -287548,6 +287720,10 @@ function OperatorDashboard({
287548
287720
  flexDirection: "row",
287549
287721
  gap: 2,
287550
287722
  children: [
287723
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
287724
+ fg: agentMode === "plan" ? colors2.warning : colors2.primary,
287725
+ children: agentMode === "plan" ? "PLAN" : "DEFAULT"
287726
+ }, undefined, false, undefined, this),
287551
287727
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
287552
287728
  fg: operatorState.requireApproval ? colors2.warning : colors2.primary,
287553
287729
  children: operatorState.requireApproval ? "APPROVAL ON" : "APPROVAL OFF"
@@ -287591,7 +287767,7 @@ function OperatorDashboard({
287591
287767
  focused: status === "running" ? selectedQueueIndex < 0 : resolveInputFocused(status, stack.length, externalDialogOpen),
287592
287768
  status: status === "waiting" ? "running" : status,
287593
287769
  mode: "operator",
287594
- operatorMode: operatorState.mode,
287770
+ operatorMode: agentMode === "plan" ? "plan" : operatorState.mode,
287595
287771
  verboseMode,
287596
287772
  expandedLogs,
287597
287773
  pendingApproval: currentPending,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pensar/apex",
3
- "version": "0.0.99",
3
+ "version": "0.0.100-canary.826dfe3c",
4
4
  "description": "AI-powered penetration testing CLI tool with terminal UI",
5
5
  "module": "src/tui/index.tsx",
6
6
  "main": "build/index.js",