clawfast 2.2.1 → 2.4.0

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 (3) hide show
  1. package/README.md +4 -6
  2. package/dist/clawfast.cjs +1069 -684
  3. package/package.json +1 -1
package/dist/clawfast.cjs CHANGED
@@ -199,8 +199,8 @@ var init_boot_ui = __esm({
199
199
  var require_main = __commonJS({
200
200
  "../node_modules/.pnpm/dotenv@17.4.2/node_modules/dotenv/lib/main.js"(exports2, module2) {
201
201
  "use strict";
202
- var fs8 = require("fs");
203
- var path12 = require("path");
202
+ var fs9 = require("fs");
203
+ var path13 = require("path");
204
204
  var os7 = require("os");
205
205
  var crypto2 = require("crypto");
206
206
  var TIPS = [
@@ -331,7 +331,7 @@ var require_main = __commonJS({
331
331
  if (options && options.path && options.path.length > 0) {
332
332
  if (Array.isArray(options.path)) {
333
333
  for (const filepath of options.path) {
334
- if (fs8.existsSync(filepath)) {
334
+ if (fs9.existsSync(filepath)) {
335
335
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
336
336
  }
337
337
  }
@@ -339,15 +339,15 @@ var require_main = __commonJS({
339
339
  possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
340
340
  }
341
341
  } else {
342
- possibleVaultPath = path12.resolve(process.cwd(), ".env.vault");
342
+ possibleVaultPath = path13.resolve(process.cwd(), ".env.vault");
343
343
  }
344
- if (fs8.existsSync(possibleVaultPath)) {
344
+ if (fs9.existsSync(possibleVaultPath)) {
345
345
  return possibleVaultPath;
346
346
  }
347
347
  return null;
348
348
  }
349
349
  function _resolveHome(envPath) {
350
- return envPath[0] === "~" ? path12.join(os7.homedir(), envPath.slice(1)) : envPath;
350
+ return envPath[0] === "~" ? path13.join(os7.homedir(), envPath.slice(1)) : envPath;
351
351
  }
352
352
  function _configVault(options) {
353
353
  const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
@@ -364,7 +364,7 @@ var require_main = __commonJS({
364
364
  return { parsed };
365
365
  }
366
366
  function configDotenv(options) {
367
- const dotenvPath = path12.resolve(process.cwd(), ".env");
367
+ const dotenvPath = path13.resolve(process.cwd(), ".env");
368
368
  let encoding = "utf8";
369
369
  let processEnv = process.env;
370
370
  if (options && options.processEnv != null) {
@@ -392,13 +392,13 @@ var require_main = __commonJS({
392
392
  }
393
393
  let lastError;
394
394
  const parsedAll = {};
395
- for (const path13 of optionPaths) {
395
+ for (const path14 of optionPaths) {
396
396
  try {
397
- const parsed = DotenvModule.parse(fs8.readFileSync(path13, { encoding }));
397
+ const parsed = DotenvModule.parse(fs9.readFileSync(path14, { encoding }));
398
398
  DotenvModule.populate(parsedAll, parsed, options);
399
399
  } catch (e) {
400
400
  if (debug) {
401
- _debug(`failed to load ${path13} ${e.message}`);
401
+ _debug(`failed to load ${path14} ${e.message}`);
402
402
  }
403
403
  lastError = e;
404
404
  }
@@ -411,7 +411,7 @@ var require_main = __commonJS({
411
411
  const shortPaths = [];
412
412
  for (const filePath of optionPaths) {
413
413
  try {
414
- const relative2 = path12.relative(process.cwd(), filePath);
414
+ const relative2 = path13.relative(process.cwd(), filePath);
415
415
  shortPaths.push(relative2);
416
416
  } catch (e) {
417
417
  if (debug) {
@@ -565,7 +565,7 @@ async function testNvidiaKey(key) {
565
565
  "Content-Type": "application/json"
566
566
  },
567
567
  body: JSON.stringify({
568
- model: "openai/gpt-oss-120b",
568
+ model: "mistralai/mistral-medium-3.5-128b",
569
569
  messages: [{ role: "user", content: "hi" }],
570
570
  max_tokens: 1
571
571
  })
@@ -689,7 +689,7 @@ var clawfastVersion, isDevVersion, isNewerVersion;
689
689
  var init_version = __esm({
690
690
  "src/version.ts"() {
691
691
  "use strict";
692
- clawfastVersion = () => true ? "2.2.1" : devVersionFromPackageJson();
692
+ clawfastVersion = () => true ? "2.4.0" : devVersionFromPackageJson();
693
693
  isDevVersion = () => clawfastVersion().includes("-dev");
694
694
  isNewerVersion = (a, b) => {
695
695
  const parse3 = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
@@ -1138,7 +1138,9 @@ function bannerSections(opts) {
1138
1138
  }
1139
1139
  function buildBanner(_systemPromptChars, version3 = "1") {
1140
1140
  const { artRows, slogan, panel } = bannerSections({ version: version3 });
1141
- return "\n" + ["", ...artRows, "", center(slogan, vlen(panel[0])), "", ...panel].join("\n") + "\n";
1141
+ return "\n" + ["", ...artRows, "", center(slogan, vlen(panel[0])), "", ...panel].join(
1142
+ "\n"
1143
+ ) + "\n";
1142
1144
  }
1143
1145
  async function playBanner(opts = {}) {
1144
1146
  const { artRows, slogan, panel } = bannerSections(opts);
@@ -1146,7 +1148,9 @@ async function playBanner(opts = {}) {
1146
1148
  process.stdout.write("\n");
1147
1149
  await revealLines(artRows, 55);
1148
1150
  process.stdout.write("\n");
1149
- const padLeft = " ".repeat(Math.max(0, Math.floor((width - vlen(slogan)) / 2)));
1151
+ const padLeft = " ".repeat(
1152
+ Math.max(0, Math.floor((width - vlen(slogan)) / 2))
1153
+ );
1150
1154
  await typeOut(SLOGAN, {
1151
1155
  perChar: 14,
1152
1156
  render: (s) => padLeft + gradient(s, { bold: true })
@@ -1154,8 +1158,10 @@ async function playBanner(opts = {}) {
1154
1158
  process.stdout.write("\n");
1155
1159
  await sweepRule(width);
1156
1160
  for (const check2 of BOOT_CHECKS) {
1157
- process.stdout.write(` ${paint("[\u2713]", PAL.green, { bold: true })} ${dim(check2)}
1158
- `);
1161
+ process.stdout.write(
1162
+ ` ${paint("[\u2713]", PAL.green, { bold: true })} ${dim(check2)}
1163
+ `
1164
+ );
1159
1165
  await sleep(110);
1160
1166
  }
1161
1167
  process.stdout.write("\n");
@@ -1261,6 +1267,12 @@ var init_news = __esm({
1261
1267
  init_ui();
1262
1268
  init_theme();
1263
1269
  NEWS = {
1270
+ "2.3.0": [
1271
+ "Linux liberado e turbinado: o clawfast detecta Kali/Linux (e se e root) e entra em modo FULL-POWER \u2014 shell real, sockets, apt/pip/pipx, systemctl e todo o arsenal Kali (nmap, masscan, sqlmap, metasploit, hydra, hashcat, nuclei, gobuster/ffuf...).",
1272
+ "Python 100% liberado e scan a vontade: portas, faixas de IP/CIDR, servicos/versoes e ate numeros (OSINT de telefone com phoneinfoga). Tudo o que o SO permite, ele faz.",
1273
+ 'Prompt do Kali repassado pra voce: quando uma ferramenta/instalador/login para e pede algo (criar conta, y/n, senha do sudo, OTP), o clawfast avisa exatamente o que "o kali" esta pedindo; voce digita aqui e ele repassa pro programa pelo stdin e segue.',
1274
+ "Escopo liberado por padrao no Linux: http_request e o toolkit de recon nao exigem mais editar scope.txt. Volte ao restrito com CLAWFAST_STRICT_SCOPE=1; force liberado em qualquer SO com CLAWFAST_UNRESTRICTED=1."
1275
+ ],
1264
1276
  "2.1.0": [
1265
1277
  'Novo visual "Verde Matrix Elite": gradiente verde\u2192teal\u2192ciano em todo o terminal. Banner cinematografico no boot \u2014 o wordmark desce em cortina, o slogan se datilografa e o painel "ACESSO CONCEDIDO" abaixa.',
1266
1278
  "Header de cada turno animado, spinner de boot pulsando em gradiente e caixas (prompt, /model, /nov) com bordas pesadas realcadas.",
@@ -1937,10 +1949,10 @@ function mergeDefs(...defs) {
1937
1949
  function cloneDef(schema) {
1938
1950
  return mergeDefs(schema._zod.def);
1939
1951
  }
1940
- function getElementAtPath(obj, path12) {
1941
- if (!path12)
1952
+ function getElementAtPath(obj, path13) {
1953
+ if (!path13)
1942
1954
  return obj;
1943
- return path12.reduce((acc, key) => acc?.[key], obj);
1955
+ return path13.reduce((acc, key) => acc?.[key], obj);
1944
1956
  }
1945
1957
  function promiseAllObject(promisesObj) {
1946
1958
  const keys = Object.keys(promisesObj);
@@ -2268,11 +2280,11 @@ function explicitlyAborted(x, startIndex = 0) {
2268
2280
  }
2269
2281
  return false;
2270
2282
  }
2271
- function prefixIssues(path12, issues) {
2283
+ function prefixIssues(path13, issues) {
2272
2284
  return issues.map((iss) => {
2273
2285
  var _a25;
2274
2286
  (_a25 = iss).path ?? (_a25.path = []);
2275
- iss.path.unshift(path12);
2287
+ iss.path.unshift(path13);
2276
2288
  return iss;
2277
2289
  });
2278
2290
  }
@@ -2490,16 +2502,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
2490
2502
  }
2491
2503
  function formatError(error51, mapper = (issue2) => issue2.message) {
2492
2504
  const fieldErrors = { _errors: [] };
2493
- const processError = (error52, path12 = []) => {
2505
+ const processError = (error52, path13 = []) => {
2494
2506
  for (const issue2 of error52.issues) {
2495
2507
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2496
- issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
2508
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
2497
2509
  } else if (issue2.code === "invalid_key") {
2498
- processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
2510
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
2499
2511
  } else if (issue2.code === "invalid_element") {
2500
- processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
2512
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
2501
2513
  } else {
2502
- const fullpath = [...path12, ...issue2.path];
2514
+ const fullpath = [...path13, ...issue2.path];
2503
2515
  if (fullpath.length === 0) {
2504
2516
  fieldErrors._errors.push(mapper(issue2));
2505
2517
  } else {
@@ -2526,17 +2538,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
2526
2538
  }
2527
2539
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
2528
2540
  const result = { errors: [] };
2529
- const processError = (error52, path12 = []) => {
2541
+ const processError = (error52, path13 = []) => {
2530
2542
  var _a25, _b18;
2531
2543
  for (const issue2 of error52.issues) {
2532
2544
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2533
- issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
2545
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
2534
2546
  } else if (issue2.code === "invalid_key") {
2535
- processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
2547
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
2536
2548
  } else if (issue2.code === "invalid_element") {
2537
- processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
2549
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
2538
2550
  } else {
2539
- const fullpath = [...path12, ...issue2.path];
2551
+ const fullpath = [...path13, ...issue2.path];
2540
2552
  if (fullpath.length === 0) {
2541
2553
  result.errors.push(mapper(issue2));
2542
2554
  continue;
@@ -2568,8 +2580,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
2568
2580
  }
2569
2581
  function toDotPath(_path) {
2570
2582
  const segs = [];
2571
- const path12 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2572
- for (const seg of path12) {
2583
+ const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2584
+ for (const seg of path13) {
2573
2585
  if (typeof seg === "number")
2574
2586
  segs.push(`[${seg}]`);
2575
2587
  else if (typeof seg === "symbol")
@@ -16072,13 +16084,13 @@ function resolveRef(ref, ctx) {
16072
16084
  if (!ref.startsWith("#")) {
16073
16085
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
16074
16086
  }
16075
- const path12 = ref.slice(1).split("/").filter(Boolean);
16076
- if (path12.length === 0) {
16087
+ const path13 = ref.slice(1).split("/").filter(Boolean);
16088
+ if (path13.length === 0) {
16077
16089
  return ctx.rootSchema;
16078
16090
  }
16079
16091
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
16080
- if (path12[0] === defsKey) {
16081
- const key = path12[1];
16092
+ if (path13[0] === defsKey) {
16093
+ const key = path13[1];
16082
16094
  if (!key || !ctx.defs[key]) {
16083
16095
  throw new Error(`Reference not found: ${ref}`);
16084
16096
  }
@@ -17267,8 +17279,8 @@ var init_parseUtil = __esm({
17267
17279
  init_errors3();
17268
17280
  init_en2();
17269
17281
  makeIssue = (params) => {
17270
- const { data, path: path12, errorMaps, issueData } = params;
17271
- const fullPath = [...path12, ...issueData.path || []];
17282
+ const { data, path: path13, errorMaps, issueData } = params;
17283
+ const fullPath = [...path13, ...issueData.path || []];
17272
17284
  const fullIssue = {
17273
17285
  ...issueData,
17274
17286
  path: fullPath
@@ -17551,11 +17563,11 @@ var init_types = __esm({
17551
17563
  init_parseUtil();
17552
17564
  init_util2();
17553
17565
  ParseInputLazyPath = class {
17554
- constructor(parent, value, path12, key) {
17566
+ constructor(parent, value, path13, key) {
17555
17567
  this._cachedPath = [];
17556
17568
  this.parent = parent;
17557
17569
  this.data = value;
17558
- this._path = path12;
17570
+ this._path = path13;
17559
17571
  this._key = key;
17560
17572
  }
17561
17573
  get path() {
@@ -23594,8 +23606,8 @@ var require_auth_config = __commonJS({
23594
23606
  writeAuthConfig: () => writeAuthConfig
23595
23607
  });
23596
23608
  module2.exports = __toCommonJS(auth_config_exports);
23597
- var fs8 = __toESM2(require("fs"));
23598
- var path12 = __toESM2(require("path"));
23609
+ var fs9 = __toESM2(require("fs"));
23610
+ var path13 = __toESM2(require("path"));
23599
23611
  var import_token_util = require_token_util();
23600
23612
  function getAuthConfigPath() {
23601
23613
  const dataDir = (0, import_token_util.getVercelDataDir)();
@@ -23604,15 +23616,15 @@ var require_auth_config = __commonJS({
23604
23616
  `Unable to find Vercel CLI data directory. Your platform: ${process.platform}. Supported: darwin, linux, win32.`
23605
23617
  );
23606
23618
  }
23607
- return path12.join(dataDir, "auth.json");
23619
+ return path13.join(dataDir, "auth.json");
23608
23620
  }
23609
23621
  function readAuthConfig() {
23610
23622
  try {
23611
23623
  const authPath = getAuthConfigPath();
23612
- if (!fs8.existsSync(authPath)) {
23624
+ if (!fs9.existsSync(authPath)) {
23613
23625
  return null;
23614
23626
  }
23615
- const content = fs8.readFileSync(authPath, "utf8");
23627
+ const content = fs9.readFileSync(authPath, "utf8");
23616
23628
  if (!content) {
23617
23629
  return null;
23618
23630
  }
@@ -23623,11 +23635,11 @@ var require_auth_config = __commonJS({
23623
23635
  }
23624
23636
  function writeAuthConfig(config3) {
23625
23637
  const authPath = getAuthConfigPath();
23626
- const authDir = path12.dirname(authPath);
23627
- if (!fs8.existsSync(authDir)) {
23628
- fs8.mkdirSync(authDir, { mode: 504, recursive: true });
23638
+ const authDir = path13.dirname(authPath);
23639
+ if (!fs9.existsSync(authDir)) {
23640
+ fs9.mkdirSync(authDir, { mode: 504, recursive: true });
23629
23641
  }
23630
- fs8.writeFileSync(authPath, JSON.stringify(config3, null, 2), { mode: 384 });
23642
+ fs9.writeFileSync(authPath, JSON.stringify(config3, null, 2), { mode: 384 });
23631
23643
  }
23632
23644
  function isValidAccessToken(authConfig, expirationBufferMs = 0) {
23633
23645
  if (!authConfig.token)
@@ -23818,8 +23830,8 @@ var require_token_util = __commonJS({
23818
23830
  saveToken: () => saveToken
23819
23831
  });
23820
23832
  module2.exports = __toCommonJS(token_util_exports);
23821
- var path12 = __toESM2(require("path"));
23822
- var fs8 = __toESM2(require("fs"));
23833
+ var path13 = __toESM2(require("path"));
23834
+ var fs9 = __toESM2(require("fs"));
23823
23835
  var import_token_error = require_token_error();
23824
23836
  var import_token_io = require_token_io();
23825
23837
  var import_auth_config = require_auth_config();
@@ -23831,7 +23843,7 @@ var require_token_util = __commonJS({
23831
23843
  if (!dataDir) {
23832
23844
  return null;
23833
23845
  }
23834
- return path12.join(dataDir, vercelFolder);
23846
+ return path13.join(dataDir, vercelFolder);
23835
23847
  }
23836
23848
  async function getVercelToken2(options) {
23837
23849
  const authConfig = (0, import_auth_config.readAuthConfig)();
@@ -23907,13 +23919,13 @@ var require_token_util = __commonJS({
23907
23919
  "Unable to find project root directory. Have you linked your project with `vc link?`"
23908
23920
  );
23909
23921
  }
23910
- const prjPath = path12.join(dir, ".vercel", "project.json");
23911
- if (!fs8.existsSync(prjPath)) {
23922
+ const prjPath = path13.join(dir, ".vercel", "project.json");
23923
+ if (!fs9.existsSync(prjPath)) {
23912
23924
  throw new import_token_error.VercelOidcTokenError(
23913
23925
  "project.json not found, have you linked your project with `vc link?`"
23914
23926
  );
23915
23927
  }
23916
- const prj = JSON.parse(fs8.readFileSync(prjPath, "utf8"));
23928
+ const prj = JSON.parse(fs9.readFileSync(prjPath, "utf8"));
23917
23929
  if (typeof prj.projectId !== "string" && typeof prj.orgId !== "string") {
23918
23930
  throw new TypeError(
23919
23931
  "Expected a string-valued projectId property. Try running `vc link` to re-link your project."
@@ -23928,11 +23940,11 @@ var require_token_util = __commonJS({
23928
23940
  "Unable to find user data directory. Please reach out to Vercel support."
23929
23941
  );
23930
23942
  }
23931
- const tokenPath = path12.join(dir, "com.vercel.token", `${projectId}.json`);
23943
+ const tokenPath = path13.join(dir, "com.vercel.token", `${projectId}.json`);
23932
23944
  const tokenJson = JSON.stringify(token);
23933
- fs8.mkdirSync(path12.dirname(tokenPath), { mode: 504, recursive: true });
23934
- fs8.writeFileSync(tokenPath, tokenJson);
23935
- fs8.chmodSync(tokenPath, 432);
23945
+ fs9.mkdirSync(path13.dirname(tokenPath), { mode: 504, recursive: true });
23946
+ fs9.writeFileSync(tokenPath, tokenJson);
23947
+ fs9.chmodSync(tokenPath, 432);
23936
23948
  return;
23937
23949
  }
23938
23950
  function loadToken(projectId) {
@@ -23942,11 +23954,11 @@ var require_token_util = __commonJS({
23942
23954
  "Unable to find user data directory. Please reach out to Vercel support."
23943
23955
  );
23944
23956
  }
23945
- const tokenPath = path12.join(dir, "com.vercel.token", `${projectId}.json`);
23946
- if (!fs8.existsSync(tokenPath)) {
23957
+ const tokenPath = path13.join(dir, "com.vercel.token", `${projectId}.json`);
23958
+ if (!fs9.existsSync(tokenPath)) {
23947
23959
  return null;
23948
23960
  }
23949
- const token = JSON.parse(fs8.readFileSync(tokenPath, "utf8"));
23961
+ const token = JSON.parse(fs9.readFileSync(tokenPath, "utf8"));
23950
23962
  assertVercelOidcTokenResponse(token);
23951
23963
  return token;
23952
23964
  }
@@ -35972,7 +35984,7 @@ function createOpenRouter(options = {}) {
35972
35984
  );
35973
35985
  const createChatModel = (modelId, settings = {}) => new OpenRouterChatLanguageModel(modelId, settings, {
35974
35986
  provider: "openrouter.chat",
35975
- url: ({ path: path12 }) => `${baseURL}${path12}`,
35987
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
35976
35988
  headers: getHeaders,
35977
35989
  compatibility,
35978
35990
  fetch: options.fetch,
@@ -35980,7 +35992,7 @@ function createOpenRouter(options = {}) {
35980
35992
  });
35981
35993
  const createCompletionModel = (modelId, settings = {}) => new OpenRouterCompletionLanguageModel(modelId, settings, {
35982
35994
  provider: "openrouter.completion",
35983
- url: ({ path: path12 }) => `${baseURL}${path12}`,
35995
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
35984
35996
  headers: getHeaders,
35985
35997
  compatibility,
35986
35998
  fetch: options.fetch,
@@ -35988,21 +36000,21 @@ function createOpenRouter(options = {}) {
35988
36000
  });
35989
36001
  const createEmbeddingModel = (modelId, settings = {}) => new OpenRouterEmbeddingModel(modelId, settings, {
35990
36002
  provider: "openrouter.embedding",
35991
- url: ({ path: path12 }) => `${baseURL}${path12}`,
36003
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
35992
36004
  headers: getHeaders,
35993
36005
  fetch: options.fetch,
35994
36006
  extraBody: options.extraBody
35995
36007
  });
35996
36008
  const createImageModel = (modelId, settings = {}) => new OpenRouterImageModel(modelId, settings, {
35997
36009
  provider: "openrouter.image",
35998
- url: ({ path: path12 }) => `${baseURL}${path12}`,
36010
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
35999
36011
  headers: getHeaders,
36000
36012
  fetch: options.fetch,
36001
36013
  extraBody: options.extraBody
36002
36014
  });
36003
36015
  const createVideoModel = (modelId, settings = {}) => new OpenRouterVideoModel(modelId, settings, {
36004
36016
  provider: "openrouter.video",
36005
- url: ({ path: path12 }) => `${baseURL}${path12}`,
36017
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
36006
36018
  headers: getHeaders,
36007
36019
  fetch: options.fetch,
36008
36020
  extraBody: options.extraBody
@@ -40538,37 +40550,37 @@ function createOpenAI(options = {}) {
40538
40550
  );
40539
40551
  const createChatModel = (modelId) => new OpenAIChatLanguageModel(modelId, {
40540
40552
  provider: `${providerName}.chat`,
40541
- url: ({ path: path12 }) => `${baseURL}${path12}`,
40553
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
40542
40554
  headers: getHeaders,
40543
40555
  fetch: options.fetch
40544
40556
  });
40545
40557
  const createCompletionModel = (modelId) => new OpenAICompletionLanguageModel(modelId, {
40546
40558
  provider: `${providerName}.completion`,
40547
- url: ({ path: path12 }) => `${baseURL}${path12}`,
40559
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
40548
40560
  headers: getHeaders,
40549
40561
  fetch: options.fetch
40550
40562
  });
40551
40563
  const createEmbeddingModel = (modelId) => new OpenAIEmbeddingModel(modelId, {
40552
40564
  provider: `${providerName}.embedding`,
40553
- url: ({ path: path12 }) => `${baseURL}${path12}`,
40565
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
40554
40566
  headers: getHeaders,
40555
40567
  fetch: options.fetch
40556
40568
  });
40557
40569
  const createImageModel = (modelId) => new OpenAIImageModel(modelId, {
40558
40570
  provider: `${providerName}.image`,
40559
- url: ({ path: path12 }) => `${baseURL}${path12}`,
40571
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
40560
40572
  headers: getHeaders,
40561
40573
  fetch: options.fetch
40562
40574
  });
40563
40575
  const createTranscriptionModel = (modelId) => new OpenAITranscriptionModel(modelId, {
40564
40576
  provider: `${providerName}.transcription`,
40565
- url: ({ path: path12 }) => `${baseURL}${path12}`,
40577
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
40566
40578
  headers: getHeaders,
40567
40579
  fetch: options.fetch
40568
40580
  });
40569
40581
  const createSpeechModel = (modelId) => new OpenAISpeechModel(modelId, {
40570
40582
  provider: `${providerName}.speech`,
40571
- url: ({ path: path12 }) => `${baseURL}${path12}`,
40583
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
40572
40584
  headers: getHeaders,
40573
40585
  fetch: options.fetch
40574
40586
  });
@@ -40583,7 +40595,7 @@ function createOpenAI(options = {}) {
40583
40595
  const createResponsesModel = (modelId) => {
40584
40596
  return new OpenAIResponsesLanguageModel(modelId, {
40585
40597
  provider: `${providerName}.responses`,
40586
- url: ({ path: path12 }) => `${baseURL}${path12}`,
40598
+ url: ({ path: path13 }) => `${baseURL}${path13}`,
40587
40599
  headers: getHeaders,
40588
40600
  fetch: options.fetch,
40589
40601
  fileIdPrefixes: ["file-"]
@@ -45808,6 +45820,9 @@ function resolveLanguageModel2(key) {
45808
45820
  if (isOpenRouterDynamicKey(key)) {
45809
45821
  return openrouter2(openRouterSlugFromKey(key));
45810
45822
  }
45823
+ if (isNvidiaDynamicKey(key)) {
45824
+ return nvidia.chat(nvidiaSlugFromKey(key));
45825
+ }
45811
45826
  return myProvider.languageModel(key);
45812
45827
  }
45813
45828
  async function listOpenRouterModels(signal) {
@@ -45833,7 +45848,25 @@ async function listOpenRouterModels(signal) {
45833
45848
  };
45834
45849
  }).filter((m) => m !== null).sort((a, b) => a.name.localeCompare(b.name));
45835
45850
  }
45836
- var isRecord, isXaiModelSlug, isGeminiModelSlug, requestCanRouteToXai, requestCanRouteToGemini, hasOwnEncryptedContent, stripEncryptedContent, sanitizeOpenRouterRequestForXai, hasJsonRefKey, wrapToolContentIfGeminiRefSensitive, sanitizeOpenRouterRequestForGeminiFunctionResponses, patchKimiReasoningToolCalls, OPENROUTER_METADATA_HEADER, withOpenRouterMetadataHeader, openrouterPatchFetch, openrouter2, openai2, isNvidiaMistralModel, applyNvidiaMistralConfig, nvidiaPatchFetch, nvidia, deepseek, kimi, buildProviderMap, hasEnvValue, isDeepSeekEnabled, isKimiEnabled, CLI_MODEL_CHAIN, baseProviders, modelCutoffDates, modelDisplayNames, getModelDisplayName, getModelCutoffDate, myProvider, OPENROUTER_KEY_PREFIX, hasOpenRouterKey, isOpenRouterDynamicKey, openRouterSlugFromKey, openRouterKeyForSlug;
45851
+ async function listNvidiaModels(signal) {
45852
+ const key = process.env.NVIDIA_API_KEY?.trim();
45853
+ if (!key) throw new Error("NVIDIA_API_KEY n\xE3o configurada");
45854
+ const baseURL = process.env.NVIDIA_BASE_URL || "https://integrate.api.nvidia.com/v1";
45855
+ const res = await fetch(`${baseURL.replace(/\/$/, "")}/models`, {
45856
+ headers: { Authorization: `Bearer ${key}` },
45857
+ signal
45858
+ });
45859
+ if (!res.ok) {
45860
+ throw new Error(`NVIDIA /models falhou: HTTP ${res.status}`);
45861
+ }
45862
+ const json3 = await res.json();
45863
+ const data = Array.isArray(json3.data) ? json3.data : [];
45864
+ return data.map((raw) => {
45865
+ if (!isRecord(raw) || typeof raw.id !== "string") return null;
45866
+ return { id: raw.id, name: raw.id };
45867
+ }).filter((m) => m !== null).sort((a, b) => a.id.localeCompare(b.id));
45868
+ }
45869
+ var isRecord, isXaiModelSlug, isGeminiModelSlug, requestCanRouteToXai, requestCanRouteToGemini, hasOwnEncryptedContent, stripEncryptedContent, sanitizeOpenRouterRequestForXai, hasJsonRefKey, wrapToolContentIfGeminiRefSensitive, sanitizeOpenRouterRequestForGeminiFunctionResponses, patchKimiReasoningToolCalls, OPENROUTER_METADATA_HEADER, withOpenRouterMetadataHeader, openrouterPatchFetch, openrouter2, openai2, isNvidiaMistralModel, applyNvidiaMistralConfig, nvidiaPatchFetch, nvidia, deepseek, kimi, buildProviderMap, hasEnvValue, isDeepSeekEnabled, isKimiEnabled, CLI_MODEL_CHAIN, baseProviders, modelCutoffDates, modelDisplayNames, getModelDisplayName, getModelCutoffDate, myProvider, OPENROUTER_KEY_PREFIX, hasOpenRouterKey, isOpenRouterDynamicKey, openRouterSlugFromKey, openRouterKeyForSlug, NVIDIA_KEY_PREFIX, hasNvidiaKey, isNvidiaDynamicKey, nvidiaSlugFromKey, nvidiaKeyForSlug;
45837
45870
  var init_providers = __esm({
45838
45871
  "../lib/ai/providers.ts"() {
45839
45872
  "use strict";
@@ -46064,7 +46097,6 @@ var init_providers = __esm({
46064
46097
  "model-nvidia-mistral-medium-3.5": nvidia.chat(
46065
46098
  "mistralai/mistral-medium-3.5-128b"
46066
46099
  ),
46067
- "model-nvidia-gpt-oss-120b": nvidia.chat("openai/gpt-oss-120b"),
46068
46100
  "model-nvidia-glm-5.1": nvidia.chat("z-ai/glm-5.1"),
46069
46101
  "model-nvidia-qwen3.5-397b": nvidia.chat("qwen/qwen3.5-397b-a17b"),
46070
46102
  // Extra explicit keys for the CLI.
@@ -46102,8 +46134,6 @@ var init_providers = __esm({
46102
46134
  ...hasEnvValue("NVIDIA_API_KEY") ? [
46103
46135
  "model-nvidia-mistral-medium-3.5",
46104
46136
  // NVIDIA mistralai/mistral-medium-3.5-128b
46105
- "model-nvidia-gpt-oss-120b",
46106
- // NVIDIA openai/gpt-oss-120b
46107
46137
  "model-nvidia-glm-5.1",
46108
46138
  // NVIDIA z-ai/glm-5.1
46109
46139
  "model-nvidia-qwen3.5-397b"
@@ -46129,7 +46159,6 @@ var init_providers = __esm({
46129
46159
  "model-opus-4.6": "May 2025",
46130
46160
  "model-kimi-k2.6": "April 2024",
46131
46161
  "model-nvidia-mistral-medium-3.5": "Unknown",
46132
- "model-nvidia-gpt-oss-120b": "June 2024",
46133
46162
  "model-nvidia-glm-5.1": "Unknown",
46134
46163
  "model-nvidia-qwen3.5-397b": "Unknown",
46135
46164
  "model-deepseek-proxy": "July 2024",
@@ -46153,7 +46182,6 @@ var init_providers = __esm({
46153
46182
  "model-opus-4.6": "Anthropic Claude Opus 4.6",
46154
46183
  "model-kimi-k2.6": "Moonshot Kimi K2.6",
46155
46184
  "model-nvidia-mistral-medium-3.5": "NVIDIA - Mistral Medium 3.5",
46156
- "model-nvidia-gpt-oss-120b": "NVIDIA - OpenAI GPT-OSS 120B",
46157
46185
  "model-nvidia-glm-5.1": "NVIDIA - Z.ai GLM 5.1",
46158
46186
  "model-nvidia-qwen3.5-397b": "NVIDIA - Qwen3.5 397B A17B",
46159
46187
  "model-deepseek-proxy": "DeepSeek (sessao web logada / deepsproxy)",
@@ -46180,6 +46208,11 @@ var init_providers = __esm({
46180
46208
  isOpenRouterDynamicKey = (key) => key.startsWith(OPENROUTER_KEY_PREFIX);
46181
46209
  openRouterSlugFromKey = (key) => key.slice(OPENROUTER_KEY_PREFIX.length);
46182
46210
  openRouterKeyForSlug = (slug) => `${OPENROUTER_KEY_PREFIX}${slug}`;
46211
+ NVIDIA_KEY_PREFIX = "nvidia:";
46212
+ hasNvidiaKey = () => Boolean(process.env.NVIDIA_API_KEY?.trim());
46213
+ isNvidiaDynamicKey = (key) => key.startsWith(NVIDIA_KEY_PREFIX);
46214
+ nvidiaSlugFromKey = (key) => key.slice(NVIDIA_KEY_PREFIX.length);
46215
+ nvidiaKeyForSlug = (slug) => `${NVIDIA_KEY_PREFIX}${slug}`;
46183
46216
  }
46184
46217
  });
46185
46218
 
@@ -49192,8 +49225,8 @@ var init_background_process_tracker = __esm({
49192
49225
  /**
49193
49226
  * Normalize file path for comparison
49194
49227
  */
49195
- normalizePath(path12) {
49196
- let normalized = path12.trim().replace(/\/+/g, "/");
49228
+ normalizePath(path13) {
49229
+ let normalized = path13.trim().replace(/\/+/g, "/");
49197
49230
  if (normalized.startsWith("./")) {
49198
49231
  normalized = normalized.slice(2);
49199
49232
  }
@@ -49431,8 +49464,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
49431
49464
  return decodedFile;
49432
49465
  };
49433
49466
  }
49434
- function normalizeWindowsPath(path12) {
49435
- return path12.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
49467
+ function normalizeWindowsPath(path13) {
49468
+ return path13.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
49436
49469
  }
49437
49470
  var import_path;
49438
49471
  var init_module_node = __esm({
@@ -52321,9 +52354,9 @@ async function addSourceContext(frames) {
52321
52354
  LRU_FILE_CONTENTS_CACHE.reduce();
52322
52355
  return frames;
52323
52356
  }
52324
- function getContextLinesFromFile(path12, ranges, output) {
52357
+ function getContextLinesFromFile(path13, ranges, output) {
52325
52358
  return new Promise((resolve2) => {
52326
- const stream = (0, import_node_fs5.createReadStream)(path12);
52359
+ const stream = (0, import_node_fs5.createReadStream)(path13);
52327
52360
  const lineReaded = (0, import_node_readline2.createInterface)({
52328
52361
  input: stream
52329
52362
  });
@@ -52338,7 +52371,7 @@ function getContextLinesFromFile(path12, ranges, output) {
52338
52371
  let rangeStart = range[0];
52339
52372
  let rangeEnd = range[1];
52340
52373
  function onStreamError() {
52341
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path12, 1);
52374
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path13, 1);
52342
52375
  lineReaded.close();
52343
52376
  lineReaded.removeAllListeners();
52344
52377
  destroyStreamAndResolve();
@@ -52399,8 +52432,8 @@ function clearLineContext(frame3) {
52399
52432
  delete frame3.context_line;
52400
52433
  delete frame3.post_context;
52401
52434
  }
52402
- function shouldSkipContextLinesForFile(path12) {
52403
- return path12.startsWith("node:") || path12.endsWith(".min.js") || path12.endsWith(".min.cjs") || path12.endsWith(".min.mjs") || path12.startsWith("data:");
52435
+ function shouldSkipContextLinesForFile(path13) {
52436
+ return path13.startsWith("node:") || path13.endsWith(".min.js") || path13.endsWith(".min.cjs") || path13.endsWith(".min.mjs") || path13.startsWith("data:");
52404
52437
  }
52405
52438
  function shouldSkipContextLinesForFrame(frame3) {
52406
52439
  if (void 0 !== frame3.lineno && frame3.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -67810,8 +67843,8 @@ var init_logger2 = __esm({
67810
67843
  });
67811
67844
 
67812
67845
  // ../lib/ai/tools/file.ts
67813
- function isSpritPath(path12) {
67814
- return path12.split(/[\\/]/).some((segment) => segment.toLowerCase() === "sprit");
67846
+ function isSpritPath(path13) {
67847
+ return path13.split(/[\\/]/).some((segment) => segment.toLowerCase() === "sprit");
67815
67848
  }
67816
67849
  function getViewSandboxType(sandbox) {
67817
67850
  return isCentrifugoSandbox(sandbox) ? "centrifugo" : "e2b";
@@ -67842,7 +67875,7 @@ function captureFileViewImageUsage(args) {
67842
67875
  const {
67843
67876
  context: context2,
67844
67877
  sandbox,
67845
- path: path12,
67878
+ path: path13,
67846
67879
  outcome,
67847
67880
  durationMs,
67848
67881
  mediaType,
@@ -67860,7 +67893,7 @@ function captureFileViewImageUsage(args) {
67860
67893
  model: getActiveModelName(context2),
67861
67894
  configured_model: context2.modelName,
67862
67895
  sandbox_type: getViewSandboxType(sandbox),
67863
- file_extension: getFileExtension(path12),
67896
+ file_extension: getFileExtension(path13),
67864
67897
  outcome,
67865
67898
  success: outcome === "success",
67866
67899
  duration_ms: durationMs,
@@ -67934,22 +67967,22 @@ async function runSandboxCommand(sandbox, command, envVars, timeoutMs = 6e4) {
67934
67967
  function isWindowsSandbox(sandbox) {
67935
67968
  return isCentrifugoSandbox(sandbox) && sandbox.isWindows();
67936
67969
  }
67937
- function getWindowsNativePath(path12) {
67938
- if (/^[A-Za-z]:[\\/]/.test(path12)) return path12;
67939
- if (path12.startsWith("/tmp/")) {
67940
- return `C:\\temp${path12.slice(4).replace(/\//g, "\\")}`;
67970
+ function getWindowsNativePath(path13) {
67971
+ if (/^[A-Za-z]:[\\/]/.test(path13)) return path13;
67972
+ if (path13.startsWith("/tmp/")) {
67973
+ return `C:\\temp${path13.slice(4).replace(/\//g, "\\")}`;
67941
67974
  }
67942
- return path12.replace(/\//g, "\\");
67975
+ return path13.replace(/\//g, "\\");
67943
67976
  }
67944
- function getPythonPathForSandbox(sandbox, path12) {
67945
- return isWindowsSandbox(sandbox) ? getWindowsNativePath(path12) : path12;
67977
+ function getPythonPathForSandbox(sandbox, path13) {
67978
+ return isWindowsSandbox(sandbox) ? getWindowsNativePath(path13) : path13;
67946
67979
  }
67947
- function toWindowsBashPath(path12) {
67948
- const drive = path12.match(/^([A-Za-z]):[\\/](.*)$/);
67980
+ function toWindowsBashPath(path13) {
67981
+ const drive = path13.match(/^([A-Za-z]):[\\/](.*)$/);
67949
67982
  if (drive) {
67950
67983
  return `/${drive[1].toLowerCase()}/${drive[2].replace(/\\/g, "/")}`;
67951
67984
  }
67952
- return path12.replace(/\\/g, "/");
67985
+ return path13.replace(/\\/g, "/");
67953
67986
  }
67954
67987
  async function detectSandboxShell(sandbox) {
67955
67988
  if (!isWindowsSandbox(sandbox)) return "bash";
@@ -67988,8 +68021,8 @@ PY`;
67988
68021
  }
67989
68022
  }
67990
68023
  }
67991
- async function getSandboxFileState(sandbox, path12) {
67992
- const pythonPath = getPythonPathForSandbox(sandbox, path12);
68024
+ async function getSandboxFileState(sandbox, path13) {
68025
+ const pythonPath = getPythonPathForSandbox(sandbox, path13);
67993
68026
  const result = await runPythonScript(
67994
68027
  sandbox,
67995
68028
  FILE_STATE_SCRIPT,
@@ -68006,23 +68039,23 @@ async function getSandboxFileState(sandbox, path12) {
68006
68039
  if (result.exitCode !== 0) {
68007
68040
  return {
68008
68041
  kind: "unknown",
68009
- path: path12,
68042
+ path: path13,
68010
68043
  error: result.stderr || result.stdout || "file state command failed"
68011
68044
  };
68012
68045
  }
68013
68046
  try {
68014
68047
  const payload = JSON.parse(result.stdout.trim());
68015
68048
  if (payload.kind === "file" && typeof payload.sizeBytes === "number" && Number.isFinite(payload.sizeBytes)) {
68016
- return { ...payload, path: path12 };
68049
+ return { ...payload, path: path13 };
68017
68050
  }
68018
68051
  if (payload.kind === "missing" || payload.kind === "not_file") {
68019
- return { ...payload, path: path12 };
68052
+ return { ...payload, path: path13 };
68020
68053
  }
68021
68054
  } catch {
68022
68055
  }
68023
68056
  return {
68024
68057
  kind: "unknown",
68025
- path: path12,
68058
+ path: path13,
68026
68059
  error: result.stderr || result.stdout || "invalid file state response"
68027
68060
  };
68028
68061
  }
@@ -68054,8 +68087,8 @@ ${numberedContent}${truncatedNotice}${footerNotice}`;
68054
68087
  })
68055
68088
  };
68056
68089
  }
68057
- async function readSandboxTextFile(sandbox, path12, range) {
68058
- const pythonPath = getPythonPathForSandbox(sandbox, path12);
68090
+ async function readSandboxTextFile(sandbox, path13, range) {
68091
+ const pythonPath = getPythonPathForSandbox(sandbox, path13);
68059
68092
  const envVars = {
68060
68093
  clawfast_FILE_READ_PATH: pythonPath,
68061
68094
  clawfast_FILE_READ_RANGE_START: String(range?.[0] ?? 0),
@@ -68083,40 +68116,40 @@ async function readSandboxTextFile(sandbox, path12, range) {
68083
68116
  }
68084
68117
  return payload;
68085
68118
  }
68086
- async function readSandboxTextFileWithFallback(sandbox, path12, range) {
68119
+ async function readSandboxTextFileWithFallback(sandbox, path13, range) {
68087
68120
  try {
68088
- return await readSandboxTextFile(sandbox, path12, range);
68121
+ return await readSandboxTextFile(sandbox, path13, range);
68089
68122
  } catch (error51) {
68090
68123
  const errorMessage = error51 instanceof Error ? error51.message : String(error51);
68091
68124
  if (errorMessage.startsWith("Invalid ") || errorMessage.includes("File not found")) {
68092
68125
  throw error51;
68093
68126
  }
68094
- const state = await getSandboxFileState(sandbox, path12);
68127
+ const state = await getSandboxFileState(sandbox, path13);
68095
68128
  if (state.kind === "unknown") {
68096
68129
  throw new Error(
68097
- `Unable to determine file size for ${path12}; refusing to load the file into memory. ${state.error}`
68130
+ `Unable to determine file size for ${path13}; refusing to load the file into memory. ${state.error}`
68098
68131
  );
68099
68132
  }
68100
68133
  if (state.kind === "missing") {
68101
- throw new Error(`File not found or is not a regular file: ${path12}`);
68134
+ throw new Error(`File not found or is not a regular file: ${path13}`);
68102
68135
  }
68103
68136
  if (state.kind === "not_file") {
68104
- throw new Error(`File is not a regular file: ${path12}`);
68137
+ throw new Error(`File is not a regular file: ${path13}`);
68105
68138
  }
68106
68139
  if (state.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
68107
68140
  if (range) {
68108
68141
  throw new Error(
68109
- `Unable to perform a bounded range read for ${path12}, and the file is too large to load safely (${formatBytes(state.sizeBytes)}). Use a targeted terminal command that writes a small result to a separate file.`
68142
+ `Unable to perform a bounded range read for ${path13}, and the file is too large to load safely (${formatBytes(state.sizeBytes)}). Use a targeted terminal command that writes a small result to a separate file.`
68110
68143
  );
68111
68144
  }
68112
68145
  return {
68113
- path: path12,
68146
+ path: path13,
68114
68147
  sizeBytes: state.sizeBytes,
68115
68148
  totalLines: 0,
68116
68149
  tooLarge: true
68117
68150
  };
68118
68151
  }
68119
- const fileContent = await sandbox.files.read(path12, {
68152
+ const fileContent = await sandbox.files.read(path13, {
68120
68153
  user: "user"
68121
68154
  });
68122
68155
  const lines = fileContent.split("\n");
@@ -68140,7 +68173,7 @@ async function readSandboxTextFileWithFallback(sandbox, path12, range) {
68140
68173
  const startIndex = start - 1;
68141
68174
  const endIndex = end === -1 ? lines.length : end;
68142
68175
  return {
68143
- path: path12,
68176
+ path: path13,
68144
68177
  sizeBytes: Buffer.byteLength(fileContent),
68145
68178
  totalLines: lines.length,
68146
68179
  content: lines.slice(startIndex, endIndex).join("\n"),
@@ -68148,7 +68181,7 @@ async function readSandboxTextFileWithFallback(sandbox, path12, range) {
68148
68181
  };
68149
68182
  }
68150
68183
  return {
68151
- path: path12,
68184
+ path: path13,
68152
68185
  sizeBytes: Buffer.byteLength(fileContent),
68153
68186
  totalLines: lines.length,
68154
68187
  content: fileContent,
@@ -68156,7 +68189,7 @@ async function readSandboxTextFileWithFallback(sandbox, path12, range) {
68156
68189
  };
68157
68190
  }
68158
68191
  }
68159
- async function appendSandboxTextFile(sandbox, path12, text2) {
68192
+ async function appendSandboxTextFile(sandbox, path13, text2) {
68160
68193
  const tempPath = `/tmp/clawfast_append_${Date.now()}_${Math.random().toString(36).slice(2)}.tmp`;
68161
68194
  await sandbox.files.write(tempPath, text2, {
68162
68195
  user: "user"
@@ -68165,7 +68198,7 @@ async function appendSandboxTextFile(sandbox, path12, text2) {
68165
68198
  sandbox,
68166
68199
  APPEND_TEXT_FILE_SCRIPT,
68167
68200
  {
68168
- clawfast_FILE_APPEND_TARGET_PATH: getPythonPathForSandbox(sandbox, path12),
68201
+ clawfast_FILE_APPEND_TARGET_PATH: getPythonPathForSandbox(sandbox, path13),
68169
68202
  clawfast_FILE_APPEND_SOURCE_PATH: getPythonPathForSandbox(
68170
68203
  sandbox,
68171
68204
  tempPath
@@ -68177,13 +68210,13 @@ async function appendSandboxTextFile(sandbox, path12, text2) {
68177
68210
  throw new Error(result.stderr || result.stdout || "Failed to append file");
68178
68211
  }
68179
68212
  }
68180
- async function readSandboxFileForView(sandbox, path12, includeData) {
68213
+ async function readSandboxFileForView(sandbox, path13, includeData) {
68181
68214
  if (isCentrifugoSandbox(sandbox) && sandbox.isWindows()) {
68182
68215
  throw new Error(
68183
68216
  "The view action is not available for Windows local sandboxes yet. Use a Linux/E2B sandbox or inspect the image manually."
68184
68217
  );
68185
68218
  }
68186
- const sandboxPath = getSandboxViewPath(sandbox, path12);
68219
+ const sandboxPath = getSandboxViewPath(sandbox, path13);
68187
68220
  const viewEnvVars = {
68188
68221
  clawfast_FILE_VIEW_PATH: sandboxPath,
68189
68222
  clawfast_FILE_VIEW_INCLUDE_DATA: includeData ? "1" : "0",
@@ -68527,19 +68560,19 @@ try:
68527
68560
  except OSError:
68528
68561
  pass
68529
68562
  `;
68530
- getFilename = (path12) => path12.split("/").pop() || path12;
68531
- getFileExtension = (path12) => {
68532
- const filename = getFilename(path12);
68563
+ getFilename = (path13) => path13.split("/").pop() || path13;
68564
+ getFileExtension = (path13) => {
68565
+ const filename = getFilename(path13);
68533
68566
  const dotIndex = filename.lastIndexOf(".");
68534
68567
  if (dotIndex <= 0 || dotIndex === filename.length - 1) return void 0;
68535
68568
  return filename.slice(dotIndex + 1).toLowerCase();
68536
68569
  };
68537
- getSandboxViewPath = (sandbox, path12) => {
68570
+ getSandboxViewPath = (sandbox, path13) => {
68538
68571
  const maybeSandbox = sandbox;
68539
- if (isCentrifugoSandbox(maybeSandbox) && maybeSandbox.isWindows() && path12.startsWith("/tmp/")) {
68540
- return `C:\\temp${path12.slice(4).replace(/\//g, "\\")}`;
68572
+ if (isCentrifugoSandbox(maybeSandbox) && maybeSandbox.isWindows() && path13.startsWith("/tmp/")) {
68573
+ return `C:\\temp${path13.slice(4).replace(/\//g, "\\")}`;
68541
68574
  }
68542
- return path12;
68575
+ return path13;
68543
68576
  };
68544
68577
  stripTrailingWs = (line) => line.replace(/[ \t]+$/u, "");
68545
68578
  editSchema = external_exports.object({
@@ -68613,7 +68646,7 @@ ${instructionsDescription}`,
68613
68646
  "A list of edits to be sequentially applied to the file. Required for `edit` action."
68614
68647
  )
68615
68648
  }),
68616
- execute: async ({ action, path: path12, text: text2, range, edits }) => {
68649
+ execute: async ({ action, path: path13, text: text2, range, edits }) => {
68617
68650
  try {
68618
68651
  const { sandbox } = await sandboxManager.getSandbox();
68619
68652
  switch (action) {
@@ -68623,7 +68656,7 @@ ${instructionsDescription}`,
68623
68656
  captureFileViewImageUsage({
68624
68657
  context: context2,
68625
68658
  sandbox,
68626
- path: path12,
68659
+ path: path13,
68627
68660
  outcome: "unsupported_model",
68628
68661
  durationMs: Date.now() - viewStartedAt,
68629
68662
  failureReason: "unsupported_model"
@@ -68632,26 +68665,26 @@ ${instructionsDescription}`,
68632
68665
  }
68633
68666
  let viewPayload;
68634
68667
  try {
68635
- viewPayload = await readSandboxFileForView(sandbox, path12, false);
68668
+ viewPayload = await readSandboxFileForView(sandbox, path13, false);
68636
68669
  } catch (error51) {
68637
68670
  captureFileViewImageUsage({
68638
68671
  context: context2,
68639
68672
  sandbox,
68640
- path: path12,
68673
+ path: path13,
68641
68674
  outcome: "inspection_failed",
68642
68675
  durationMs: Date.now() - viewStartedAt,
68643
68676
  failureReason: classifyFileViewError(error51)
68644
68677
  });
68645
68678
  throw error51;
68646
68679
  }
68647
- const filename = getFilename(path12);
68680
+ const filename = getFilename(path13);
68648
68681
  let previewFiles = [];
68649
68682
  let previewUploadError;
68650
68683
  try {
68651
68684
  previewFiles = await uploadViewPreviewFiles({
68652
68685
  context: context2,
68653
68686
  sandbox,
68654
- sourcePath: path12,
68687
+ sourcePath: path13,
68655
68688
  payload: viewPayload
68656
68689
  });
68657
68690
  } catch (error51) {
@@ -68665,7 +68698,7 @@ ${instructionsDescription}`,
68665
68698
  user_id: context2.userID,
68666
68699
  sandbox_type: getViewSandboxType(sandbox),
68667
68700
  file_name: filename,
68668
- source_path: path12,
68701
+ source_path: path13,
68669
68702
  kind: viewPayload.kind,
68670
68703
  media_type: viewPayload.mediaType,
68671
68704
  size_bytes: viewPayload.sizeBytes,
@@ -68676,7 +68709,7 @@ ${instructionsDescription}`,
68676
68709
  captureFileViewImageUsage({
68677
68710
  context: context2,
68678
68711
  sandbox,
68679
- path: path12,
68712
+ path: path13,
68680
68713
  outcome: "success",
68681
68714
  durationMs: Date.now() - viewStartedAt,
68682
68715
  mediaType: viewPayload.mediaType,
@@ -68686,7 +68719,7 @@ ${instructionsDescription}`,
68686
68719
  return {
68687
68720
  action: "view",
68688
68721
  content: `Viewing image file: ${filename} (${viewPayload.mediaType}, ${viewPayload.sizeBytes} bytes).`,
68689
- path: path12,
68722
+ path: path13,
68690
68723
  filename,
68691
68724
  mediaType: viewPayload.mediaType,
68692
68725
  sizeBytes: viewPayload.sizeBytes,
@@ -68696,8 +68729,8 @@ ${instructionsDescription}`,
68696
68729
  };
68697
68730
  }
68698
68731
  case "read": {
68699
- const filename = path12.split("/").pop() || path12;
68700
- const spritChunked = isSpritPath(path12);
68732
+ const filename = path13.split("/").pop() || path13;
68733
+ const spritChunked = isSpritPath(path13);
68701
68734
  let effectiveRange = range;
68702
68735
  if (spritChunked) {
68703
68736
  const start = range && range[0] > 0 ? range[0] : 1;
@@ -68710,7 +68743,7 @@ ${instructionsDescription}`,
68710
68743
  }
68711
68744
  const readPayload = await readSandboxTextFileWithFallback(
68712
68745
  sandbox,
68713
- path12,
68746
+ path13,
68714
68747
  effectiveRange
68715
68748
  );
68716
68749
  if (readPayload.tooLarge) {
@@ -68747,46 +68780,46 @@ File is too large to read in full (${formatBytes(readPayload.sizeBytes)}, ${tota
68747
68780
  if (text2 === void 0) {
68748
68781
  return { error: "text is required for write action" };
68749
68782
  }
68750
- await sandbox.files.write(path12, text2, {
68783
+ await sandbox.files.write(path13, text2, {
68751
68784
  user: "user"
68752
68785
  });
68753
- return `File written: ${path12}`;
68786
+ return `File written: ${path13}`;
68754
68787
  }
68755
68788
  case "append": {
68756
68789
  if (text2 === void 0) {
68757
68790
  return { error: "text is required for append action" };
68758
68791
  }
68759
- const existingState = await getSandboxFileState(sandbox, path12);
68792
+ const existingState = await getSandboxFileState(sandbox, path13);
68760
68793
  if (existingState.kind === "unknown") {
68761
68794
  return {
68762
- error: `Cannot append safely because the existing file size could not be determined for ${path12}. ${existingState.error}`
68795
+ error: `Cannot append safely because the existing file size could not be determined for ${path13}. ${existingState.error}`
68763
68796
  };
68764
68797
  }
68765
68798
  if (existingState.kind === "not_file") {
68766
68799
  return {
68767
- error: `Cannot append to ${path12} because it is not a file.`
68800
+ error: `Cannot append to ${path13} because it is not a file.`
68768
68801
  };
68769
68802
  }
68770
68803
  if (existingState.kind === "file" && existingState.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
68771
- await appendSandboxTextFile(sandbox, path12, text2);
68804
+ await appendSandboxTextFile(sandbox, path13, text2);
68772
68805
  return {
68773
- content: `File appended: ${path12}
68806
+ content: `File appended: ${path13}
68774
68807
  Existing file is ${formatBytes(existingState.sizeBytes)}, so the full diff preview was skipped to avoid loading the entire file into memory.`
68775
68808
  };
68776
68809
  }
68777
68810
  let existingContent = "";
68778
68811
  try {
68779
- existingContent = await sandbox.files.read(path12, {
68812
+ existingContent = await sandbox.files.read(path13, {
68780
68813
  user: "user"
68781
68814
  });
68782
68815
  } catch {
68783
68816
  }
68784
68817
  const newContent = existingContent + text2;
68785
- await sandbox.files.write(path12, newContent, {
68818
+ await sandbox.files.write(path13, newContent, {
68786
68819
  user: "user"
68787
68820
  });
68788
68821
  return {
68789
- content: `File appended: ${path12}`,
68822
+ content: `File appended: ${path13}`,
68790
68823
  originalContent: truncateOutput({
68791
68824
  content: existingContent,
68792
68825
  mode: "read-file"
@@ -68801,31 +68834,31 @@ Existing file is ${formatBytes(existingState.sizeBytes)}, so the full diff previ
68801
68834
  if (!edits || edits.length === 0) {
68802
68835
  return { error: "edits array is required for edit action" };
68803
68836
  }
68804
- const existingState = await getSandboxFileState(sandbox, path12);
68837
+ const existingState = await getSandboxFileState(sandbox, path13);
68805
68838
  if (existingState.kind === "unknown") {
68806
68839
  return {
68807
- error: `Cannot edit ${path12} safely because the file size could not be determined. ${existingState.error}`
68840
+ error: `Cannot edit ${path13} safely because the file size could not be determined. ${existingState.error}`
68808
68841
  };
68809
68842
  }
68810
68843
  if (existingState.kind === "missing") {
68811
68844
  return {
68812
- error: `Cannot edit file ${path12} - file is empty or does not exist`
68845
+ error: `Cannot edit file ${path13} - file is empty or does not exist`
68813
68846
  };
68814
68847
  }
68815
68848
  if (existingState.kind === "not_file") {
68816
- return { error: `Cannot edit ${path12} because it is not a file.` };
68849
+ return { error: `Cannot edit ${path13} because it is not a file.` };
68817
68850
  }
68818
68851
  if (existingState.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
68819
68852
  return {
68820
- error: `File ${path12} is too large for the edit action (${formatBytes(existingState.sizeBytes)}). Use a targeted shell command, restore the file from a clean source, or replace it with the write action instead of loading the whole file into memory.`
68853
+ error: `File ${path13} is too large for the edit action (${formatBytes(existingState.sizeBytes)}). Use a targeted shell command, restore the file from a clean source, or replace it with the write action instead of loading the whole file into memory.`
68821
68854
  };
68822
68855
  }
68823
- const originalContent = await sandbox.files.read(path12, {
68856
+ const originalContent = await sandbox.files.read(path13, {
68824
68857
  user: "user"
68825
68858
  });
68826
68859
  if (!originalContent) {
68827
68860
  return {
68828
- error: `Cannot edit file ${path12} - file is empty or does not exist`
68861
+ error: `Cannot edit file ${path13} - file is empty or does not exist`
68829
68862
  };
68830
68863
  }
68831
68864
  const resolvedEdits = [];
@@ -68870,7 +68903,7 @@ ${hint}` : "")
68870
68903
  }
68871
68904
  }
68872
68905
  }
68873
- await sandbox.files.write(path12, content, {
68906
+ await sandbox.files.write(path13, content, {
68874
68907
  user: "user"
68875
68908
  });
68876
68909
  const lines = content.split("\n");
@@ -68962,419 +68995,483 @@ ${numberedLines}`,
68962
68995
  }
68963
68996
  });
68964
68997
 
68965
- // ../lib/utils/error-redaction.ts
68966
- var REDACTED_VALUE, SENSITIVE_FIELD_PATTERN, ENV_SECRET_PATTERN, redactSensitiveErrorMessage, stringifyRedactedError;
68967
- var init_error_redaction = __esm({
68968
- "../lib/utils/error-redaction.ts"() {
68969
- "use strict";
68970
- REDACTED_VALUE = "[Redacted]";
68971
- SENSITIVE_FIELD_PATTERN = /(["']?\b(?:serviceKey|service_key|apiKey|api_key|authorization|bearer|cookie|password|secret|token)\b["']?)(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,}]+)/gi;
68972
- ENV_SECRET_PATTERN = /(["']?\b(?:CONVEX_SERVICE_ROLE_KEY|POSTHOG_API_KEY|STRIPE_SECRET_KEY)\b["']?)(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,}]+)/gi;
68973
- redactSensitiveErrorMessage = (message) => message.replace(SENSITIVE_FIELD_PATTERN, (_match, key, separator) => {
68974
- return `${key}${separator}"${REDACTED_VALUE}"`;
68975
- }).replace(ENV_SECRET_PATTERN, (_match, key, separator) => {
68976
- return `${key}${separator}"${REDACTED_VALUE}"`;
68977
- });
68978
- stringifyRedactedError = (error51) => {
68979
- const message = error51 instanceof Error ? error51.message : typeof error51 === "string" ? error51 : (() => {
68998
+ // src/tools/web-research.ts
68999
+ function decodeHtmlEntities(input) {
69000
+ return input.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (whole, code) => {
69001
+ if (code[0] === "#") {
69002
+ const num = code[1] === "x" || code[1] === "X" ? Number.parseInt(code.slice(2), 16) : Number.parseInt(code.slice(1), 10);
69003
+ if (Number.isFinite(num) && num > 0 && num <= 1114111) {
68980
69004
  try {
68981
- return JSON.stringify(error51);
69005
+ return String.fromCodePoint(num);
68982
69006
  } catch {
68983
- return String(error51);
69007
+ return whole;
68984
69008
  }
68985
- })();
68986
- return redactSensitiveErrorMessage(message);
68987
- };
68988
- }
68989
- });
68990
-
68991
- // ../lib/ai/tools/utils/perplexity.ts
68992
- var SEARCH_RESULT_CONTENT_MAX_TOKENS, RECENCY_MAP, PerplexityApiError, ERROR_BODY_SUMMARY_MAX_LENGTH, RETRYABLE_PERPLEXITY_STATUSES, HTML_ENTITY_MAP, normalizeWhitespace, decodeBasicHtmlEntities, stripHtml, extractHtmlTagText, redactNetworkDetails, truncateSummary, isRetryablePerplexityStatus, summarizePerplexityErrorBody, buildPerplexitySearchBody, formatSearchResults;
68993
- var init_perplexity = __esm({
68994
- "../lib/ai/tools/utils/perplexity.ts"() {
68995
- "use strict";
68996
- SEARCH_RESULT_CONTENT_MAX_TOKENS = 250;
68997
- RECENCY_MAP = {
68998
- past_day: "day",
68999
- past_week: "week",
69000
- past_month: "month",
69001
- past_year: "year"
69002
- };
69003
- PerplexityApiError = class extends Error {
69004
- constructor({
69005
- status,
69006
- statusText,
69007
- bodySummary,
69008
- retryable
69009
- }) {
69010
- const statusLabel = statusText ? `${status} ${statusText}` : `${status}`;
69011
- const summary = bodySummary ? `: ${bodySummary}` : "";
69012
- super(`Perplexity API error ${statusLabel}${summary}`);
69013
- this.name = "PerplexityApiError";
69014
- this.status = status;
69015
- this.statusText = statusText;
69016
- this.bodySummary = bodySummary;
69017
- this.retryable = retryable;
69018
69009
  }
69010
+ return whole;
69011
+ }
69012
+ return NAMED_ENTITIES[code] != null ? NAMED_ENTITIES[code] : whole;
69013
+ });
69014
+ }
69015
+ function htmlToText(html) {
69016
+ let s = html;
69017
+ s = s.replace(/<!--[\s\S]*?-->/g, " ");
69018
+ s = s.replace(/<(script|style|noscript|svg|head|template)[\s\S]*?<\/\1>/gi, " ");
69019
+ s = s.replace(
69020
+ /<\/(p|div|section|article|header|footer|li|ul|ol|tr|table|h[1-6]|blockquote|pre|nav|aside)>/gi,
69021
+ "\n"
69022
+ );
69023
+ s = s.replace(/<br\s*\/?>/gi, "\n");
69024
+ s = s.replace(/<[^>]+>/g, " ");
69025
+ s = decodeHtmlEntities(s);
69026
+ s = s.replace(/[ \t\f\v\r]+/g, " ");
69027
+ s = s.replace(/\n{3,}/g, "\n\n");
69028
+ s = s.split("\n").map((line) => line.trim()).join("\n").trim();
69029
+ return s;
69030
+ }
69031
+ function extractTitle(html) {
69032
+ const m = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
69033
+ return m ? htmlToText(m[1]).slice(0, 200) : "";
69034
+ }
69035
+ function decodeDdgHref(href) {
69036
+ let h = (href || "").trim();
69037
+ if (!h) return "";
69038
+ if (h.startsWith("//")) h = "https:" + h;
69039
+ try {
69040
+ const u = new URL(h, "https://duckduckgo.com");
69041
+ const uddg = u.searchParams.get("uddg");
69042
+ if (uddg) return uddg;
69043
+ return u.href;
69044
+ } catch {
69045
+ return h;
69046
+ }
69047
+ }
69048
+ function parseDuckDuckGoHtml(html) {
69049
+ const snippets = [];
69050
+ const snippetRe = /<a[^>]+class="[^"]*result__snippet[^"]*"[^>]*>([\s\S]*?)<\/a>/gi;
69051
+ let sm;
69052
+ while (sm = snippetRe.exec(html)) snippets.push(htmlToText(sm[1]));
69053
+ const results = [];
69054
+ const linkRe = /<a[^>]+class="[^"]*result__a[^"]*"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
69055
+ let m;
69056
+ let i = 0;
69057
+ while (m = linkRe.exec(html)) {
69058
+ const url2 = decodeDdgHref(m[1]);
69059
+ const title = htmlToText(m[2]);
69060
+ if (url2 && title && /^https?:\/\//i.test(url2)) {
69061
+ results.push({ title, url: url2, snippet: snippets[i] || "" });
69062
+ }
69063
+ i++;
69064
+ }
69065
+ return results;
69066
+ }
69067
+ function parseDuckDuckGoLite(html) {
69068
+ const results = [];
69069
+ const linkRe = /<a[^>]+class="[^"]*result-link[^"]*"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
69070
+ let m;
69071
+ while (m = linkRe.exec(html)) {
69072
+ const url2 = decodeDdgHref(m[1]);
69073
+ const title = htmlToText(m[2]);
69074
+ if (url2 && title && /^https?:\/\//i.test(url2)) {
69075
+ results.push({ title, url: url2, snippet: "" });
69076
+ }
69077
+ }
69078
+ return results;
69079
+ }
69080
+ function canonicalUrl(raw) {
69081
+ try {
69082
+ const u = new URL(raw.trim());
69083
+ if (u.protocol !== "http:" && u.protocol !== "https:") return null;
69084
+ u.hash = "";
69085
+ let s = u.href;
69086
+ if (s.endsWith("/")) s = s.slice(0, -1);
69087
+ return s;
69088
+ } catch {
69089
+ return null;
69090
+ }
69091
+ }
69092
+ function dedupeResults(results) {
69093
+ const seen = /* @__PURE__ */ new Set();
69094
+ const out3 = [];
69095
+ for (const r2 of results) {
69096
+ const key = canonicalUrl(r2.url);
69097
+ if (!key || seen.has(key)) continue;
69098
+ seen.add(key);
69099
+ out3.push(r2);
69100
+ }
69101
+ return out3;
69102
+ }
69103
+ async function mapPool(items, concurrency, fn) {
69104
+ const results = new Array(items.length);
69105
+ if (items.length === 0) return results;
69106
+ const workers = [];
69107
+ const n = Math.min(Math.max(1, concurrency), items.length);
69108
+ let next = 0;
69109
+ for (let w = 0; w < n; w++) {
69110
+ workers.push(
69111
+ (async () => {
69112
+ for (; ; ) {
69113
+ const i = next++;
69114
+ if (i >= items.length) break;
69115
+ try {
69116
+ results[i] = await fn(items[i], i);
69117
+ } catch (err) {
69118
+ results[i] = {
69119
+ error: err instanceof Error ? err.message : String(err)
69120
+ };
69121
+ }
69122
+ }
69123
+ })()
69124
+ );
69125
+ }
69126
+ await Promise.all(workers);
69127
+ return results;
69128
+ }
69129
+ async function fetchText(url2, opts) {
69130
+ const ctrl = new AbortController();
69131
+ const onAbort = () => ctrl.abort();
69132
+ if (opts.signal) {
69133
+ if (opts.signal.aborted) ctrl.abort();
69134
+ else opts.signal.addEventListener("abort", onAbort, { once: true });
69135
+ }
69136
+ const timer2 = setTimeout(() => ctrl.abort(), opts.timeoutMs);
69137
+ try {
69138
+ const resp = await fetch(url2, {
69139
+ method: "GET",
69140
+ headers: {
69141
+ "User-Agent": UA,
69142
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
69143
+ "Accept-Language": "en-US,en;q=0.9,pt-BR;q=0.8"
69144
+ },
69145
+ redirect: "follow",
69146
+ signal: ctrl.signal
69147
+ });
69148
+ let text2 = await resp.text();
69149
+ if (text2.length > MAX_RAW_BYTES) text2 = text2.slice(0, MAX_RAW_BYTES);
69150
+ return {
69151
+ status: resp.status,
69152
+ finalUrl: resp.url || url2,
69153
+ contentType: resp.headers.get("content-type") || "",
69154
+ text: text2
69019
69155
  };
69020
- ERROR_BODY_SUMMARY_MAX_LENGTH = 400;
69021
- RETRYABLE_PERPLEXITY_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
69022
- HTML_ENTITY_MAP = {
69023
- amp: "&",
69024
- gt: ">",
69025
- lt: "<",
69026
- nbsp: " ",
69027
- quot: '"',
69028
- "#160": " ",
69029
- "#39": "'"
69030
- };
69031
- normalizeWhitespace = (value) => value.replace(/\s+/g, " ").trim();
69032
- decodeBasicHtmlEntities = (value) => value.replace(/&([a-zA-Z0-9#]+);/g, (match, entity) => {
69033
- return HTML_ENTITY_MAP[entity] ?? match;
69156
+ } finally {
69157
+ clearTimeout(timer2);
69158
+ opts.signal?.removeEventListener("abort", onAbort);
69159
+ }
69160
+ }
69161
+ async function searchOneQuery(query, signal, df) {
69162
+ const enc = encodeURIComponent(query);
69163
+ const dfParam = df ? `&df=${df}` : "";
69164
+ try {
69165
+ const r2 = await fetchText(
69166
+ `https://html.duckduckgo.com/html/?q=${enc}${dfParam}`,
69167
+ { timeoutMs: SEARCH_TIMEOUT_MS, signal }
69168
+ );
69169
+ const parsed = parseDuckDuckGoHtml(r2.text);
69170
+ if (parsed.length) return parsed;
69171
+ } catch (err) {
69172
+ if (isAbort(err)) throw err;
69173
+ }
69174
+ try {
69175
+ const r2 = await fetchText(
69176
+ `https://lite.duckduckgo.com/lite/?q=${enc}${dfParam}`,
69177
+ { timeoutMs: SEARCH_TIMEOUT_MS, signal }
69178
+ );
69179
+ return parseDuckDuckGoLite(r2.text);
69180
+ } catch (err) {
69181
+ if (isAbort(err)) throw err;
69182
+ return [];
69183
+ }
69184
+ }
69185
+ async function searchQueries(queries, signal, df) {
69186
+ const lists = await mapPool(
69187
+ queries,
69188
+ Math.min(queries.length, 5),
69189
+ (q) => searchOneQuery(q, signal, df)
69190
+ );
69191
+ const merged = [];
69192
+ for (const item of lists) {
69193
+ if (Array.isArray(item)) merged.push(...item);
69194
+ }
69195
+ return dedupeResults(merged);
69196
+ }
69197
+ async function readPage(url2, signal) {
69198
+ const r2 = await fetchText(url2, { timeoutMs: READ_TIMEOUT_MS, signal });
69199
+ const text2 = isTextual(r2.contentType) ? htmlToText(r2.text) : "";
69200
+ return {
69201
+ url: url2,
69202
+ finalUrl: r2.finalUrl,
69203
+ status: r2.status,
69204
+ title: extractTitle(r2.text) || r2.finalUrl,
69205
+ text: text2,
69206
+ bytes: r2.text.length,
69207
+ contentType: r2.contentType
69208
+ };
69209
+ }
69210
+ async function jinaRead(url2, signal) {
69211
+ const key = process.env.JINA_API_KEY;
69212
+ if (!key) return null;
69213
+ try {
69214
+ const resp = await fetch(`https://r.jina.ai/${url2}`, {
69215
+ headers: { Authorization: `Bearer ${key}`, "X-Timeout": "25" },
69216
+ signal
69034
69217
  });
69035
- stripHtml = (value) => normalizeWhitespace(
69036
- decodeBasicHtmlEntities(
69037
- value.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ")
69038
- )
69218
+ if (!resp.ok) return null;
69219
+ return await resp.text();
69220
+ } catch {
69221
+ return null;
69222
+ }
69223
+ }
69224
+ function formatSearchResults(results) {
69225
+ if (!results.length) {
69226
+ return "Nenhum resultado encontrado. Refine a query (sin\xF4nimos, termos em ingl\xEAs, operadores site:/filetype:) e tente de novo.";
69227
+ }
69228
+ const shown = results.slice(0, SEARCH_MAX_RESULTS);
69229
+ const lines = [`${shown.length} resultado(s):`];
69230
+ shown.forEach((r2, i) => {
69231
+ lines.push("");
69232
+ lines.push(`${i + 1}. ${r2.title}`);
69233
+ lines.push(` ${r2.url}`);
69234
+ if (r2.snippet) lines.push(` ${truncate3(r2.snippet, 300)}`);
69235
+ });
69236
+ return lines.join("\n");
69237
+ }
69238
+ function formatResearchDigest(pages, stats) {
69239
+ const header = `Pesquisa profunda: ${stats.totalFound} candidato(s), ${stats.fetched} buscado(s) (${stats.conc ?? 1} em paralelo), ${stats.ok} ok, ${stats.failures} falha(s)` + (stats.capped ? `, capado em ${stats.maxPages} p\xE1ginas` : "") + ".";
69240
+ if (!pages.length) {
69241
+ return header + "\nNenhuma p\xE1gina retornou texto. Refine as queries ou forne\xE7a urls e tente de novo.";
69242
+ }
69243
+ const INLINE = 40;
69244
+ const lines = [header, ""];
69245
+ pages.slice(0, INLINE).forEach((p, i) => {
69246
+ lines.push(`### ${i + 1}. ${p.title}`);
69247
+ lines.push(`${p.finalUrl} (HTTP ${p.status})`);
69248
+ const extract = p.text.replace(/\n+/g, " ").trim();
69249
+ if (extract) lines.push(truncate3(extract, 600));
69250
+ lines.push("");
69251
+ });
69252
+ if (pages.length > INLINE) {
69253
+ lines.push(`\u2026 +${pages.length - INLINE} fonte(s) no relat\xF3rio completo.`);
69254
+ }
69255
+ if (stats.reportPath) {
69256
+ lines.push(`Relat\xF3rio completo salvo em: ${stats.reportPath}`);
69257
+ }
69258
+ return lines.join("\n");
69259
+ }
69260
+ async function saveResearchReport(workdir, queries, pages, stats) {
69261
+ if (!workdir || !pages.length) return null;
69262
+ try {
69263
+ const dir = import_node_path5.default.join(workdir, "reports");
69264
+ await import_node_fs6.promises.mkdir(dir, { recursive: true });
69265
+ const slug = (queries[0] || "research").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "research";
69266
+ const file2 = import_node_path5.default.join(dir, `research_${slug}_${Date.now()}.md`);
69267
+ const parts = [];
69268
+ parts.push(`# Pesquisa: ${queries.join(" / ") || "(seed urls)"}`);
69269
+ parts.push("");
69270
+ parts.push(
69271
+ `Candidatos: ${stats.totalFound} \xB7 Buscados: ${stats.fetched} \xB7 OK: ${stats.ok} \xB7 Falhas: ${stats.failures}` + (stats.capped ? ` \xB7 capado em ${stats.maxPages}` : "")
69039
69272
  );
69040
- extractHtmlTagText = (html, tagName) => {
69041
- const matches = html.matchAll(
69042
- new RegExp(`<${tagName}\\b[^>]*>([\\s\\S]*?)<\\/${tagName}>`, "gi")
69043
- );
69044
- return Array.from(matches).map((match) => stripHtml(match[1] ?? "")).filter(Boolean);
69045
- };
69046
- redactNetworkDetails = (value) => value.replace(
69047
- /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/g,
69048
- "[Redacted IP]"
69049
- ).replace(/\bRay ID:\s*[a-f0-9]+\b/gi, "Ray ID: [Redacted]");
69050
- truncateSummary = (value) => value.length > ERROR_BODY_SUMMARY_MAX_LENGTH ? `${value.slice(0, ERROR_BODY_SUMMARY_MAX_LENGTH - 1)}\u2026` : value;
69051
- isRetryablePerplexityStatus = (status) => RETRYABLE_PERPLEXITY_STATUSES.has(status);
69052
- summarizePerplexityErrorBody = (body, contentType = "") => {
69053
- const trimmed = body.trim();
69054
- if (!trimmed) return "";
69055
- let summary = "";
69056
- if (contentType.includes("json") || trimmed.startsWith("{")) {
69057
- try {
69058
- const parsed = JSON.parse(trimmed);
69059
- const error51 = typeof parsed.error === "string" ? parsed.error : parsed.error && typeof parsed.error === "object" && "message" in parsed.error && typeof parsed.error.message === "string" ? parsed.error.message : void 0;
69060
- const message = error51 || (typeof parsed.message === "string" ? parsed.message : void 0) || (typeof parsed.detail === "string" ? parsed.detail : void 0);
69061
- summary = message || trimmed;
69062
- } catch {
69063
- summary = trimmed;
69064
- }
69065
- } else if (/<[a-z][\s\S]*>/i.test(trimmed)) {
69066
- const tagSummaries = [
69067
- ...extractHtmlTagText(trimmed, "h1"),
69068
- ...extractHtmlTagText(trimmed, "p"),
69069
- ...extractHtmlTagText(trimmed, "h2")
69070
- ];
69071
- summary = tagSummaries.length > 0 ? tagSummaries.join(". ") : stripHtml(trimmed);
69072
- } else {
69073
- summary = trimmed;
69074
- }
69075
- return truncateSummary(redactNetworkDetails(normalizeWhitespace(summary)));
69076
- };
69077
- buildPerplexitySearchBody = (query, options) => {
69078
- const searchBody = {
69079
- query,
69080
- max_results: options?.maxResults ?? 10,
69081
- max_tokens_per_page: SEARCH_RESULT_CONTENT_MAX_TOKENS
69082
- };
69083
- if (options?.country) {
69084
- searchBody.country = options.country;
69085
- }
69086
- if (options?.recency) {
69087
- searchBody.search_recency_filter = options.recency;
69088
- }
69089
- return searchBody;
69090
- };
69091
- formatSearchResults = (results) => {
69092
- return results.map((result) => ({
69093
- title: result.title,
69094
- url: result.url,
69095
- content: result.snippet,
69096
- date: result.date || null,
69097
- lastUpdated: result.last_updated || null
69098
- }));
69099
- };
69273
+ parts.push("");
69274
+ pages.forEach((p, i) => {
69275
+ parts.push(`## ${i + 1}. ${p.title}`);
69276
+ parts.push(`- URL: ${p.finalUrl}`);
69277
+ parts.push(`- HTTP: ${p.status} \xB7 ${p.bytes} bytes`);
69278
+ parts.push("");
69279
+ parts.push(truncate3(p.text, 4e3));
69280
+ parts.push("");
69281
+ });
69282
+ await import_node_fs6.promises.writeFile(file2, parts.join("\n"), "utf8");
69283
+ return file2;
69284
+ } catch {
69285
+ return null;
69100
69286
  }
69101
- });
69102
-
69103
- // ../lib/ai/tools/web-search.ts
69104
- var WEB_SEARCH_COST_PER_REQUEST, PERPLEXITY_SEARCH_URL, WEB_SEARCH_MAX_ATTEMPTS, WEB_SEARCH_RETRY_BASE_DELAY_MS, WEB_SEARCH_RETRY_JITTER_MS, PERPLEXITY_QUERY_MAX_LENGTH, EMPTY_QUERY_TOOL_ERROR, QUERY_TOO_LONG_TOOL_ERROR, webSearchQuerySchema, sleep2, getRetryDelayMs, createPerplexityApiError, formatPerplexityFailureForTool, fetchPerplexitySearch, normalizeSearchQueries, createWebSearch;
69105
- var init_web_search = __esm({
69106
- "../lib/ai/tools/web-search.ts"() {
69287
+ }
69288
+ var import_node_fs6, import_node_path5, UA, SEARCH_TIMEOUT_MS, READ_TIMEOUT_MS, MAX_RAW_BYTES, OPEN_URL_MAX_CHARS, SEARCH_MAX_RESULTS, HARD_PAGE_CEILING, TIME_TO_DDG_DF, NAMED_ENTITIES, truncate3, isAbort, isTextual, TIME_ENUM, createWebSearch, createOpenUrl, createDeepResearch;
69289
+ var init_web_research = __esm({
69290
+ "src/tools/web-research.ts"() {
69107
69291
  "use strict";
69108
69292
  init_dist5();
69109
69293
  init_zod();
69110
- init_error_redaction();
69111
- init_perplexity();
69112
- WEB_SEARCH_COST_PER_REQUEST = 5e-3;
69113
- PERPLEXITY_SEARCH_URL = "https://api.perplexity.ai/search";
69114
- WEB_SEARCH_MAX_ATTEMPTS = 3;
69115
- WEB_SEARCH_RETRY_BASE_DELAY_MS = 300;
69116
- WEB_SEARCH_RETRY_JITTER_MS = 75;
69117
- PERPLEXITY_QUERY_MAX_LENGTH = 8192;
69118
- EMPTY_QUERY_TOOL_ERROR = "Error performing web search: Provide at least one non-empty query.";
69119
- QUERY_TOO_LONG_TOOL_ERROR = `Error performing web search: Each query must be ${PERPLEXITY_QUERY_MAX_LENGTH} characters or fewer.`;
69120
- webSearchQuerySchema = external_exports.string().trim().min(1).max(PERPLEXITY_QUERY_MAX_LENGTH);
69121
- sleep2 = (delayMs, signal) => {
69122
- if (delayMs <= 0) return Promise.resolve();
69123
- if (signal?.aborted) {
69124
- return Promise.reject(new DOMException("Operation aborted", "AbortError"));
69125
- }
69126
- return new Promise((resolve2, reject) => {
69127
- const cleanup = () => signal?.removeEventListener("abort", onAbort);
69128
- const onAbort = () => {
69129
- clearTimeout(timeout);
69130
- cleanup();
69131
- reject(new DOMException("Operation aborted", "AbortError"));
69132
- };
69133
- const timeout = setTimeout(() => {
69134
- cleanup();
69135
- resolve2();
69136
- }, delayMs);
69137
- signal?.addEventListener("abort", onAbort, { once: true });
69138
- });
69139
- };
69140
- getRetryDelayMs = (attemptIndex) => {
69141
- const exponentialDelay = WEB_SEARCH_RETRY_BASE_DELAY_MS * Math.pow(2, attemptIndex);
69142
- const jitter = Math.random() * WEB_SEARCH_RETRY_JITTER_MS;
69143
- return Math.round(exponentialDelay + jitter);
69144
- };
69145
- createPerplexityApiError = async (response) => {
69146
- const errorText = await response.text();
69147
- const bodySummary = summarizePerplexityErrorBody(
69148
- errorText,
69149
- response.headers.get("content-type") || ""
69150
- );
69151
- return new PerplexityApiError({
69152
- status: response.status,
69153
- statusText: response.statusText,
69154
- bodySummary,
69155
- retryable: isRetryablePerplexityStatus(response.status)
69156
- });
69157
- };
69158
- formatPerplexityFailureForTool = (error51, attempts) => {
69159
- const statusText = error51.statusText ? ` ${error51.statusText}` : "";
69160
- if (error51.retryable) {
69161
- return `Error performing web search: Perplexity search is temporarily unavailable (HTTP ${error51.status}${statusText} after ${attempts} attempts). Please retry shortly or continue without live web results if the task can proceed.`;
69162
- }
69163
- if (error51.status === 401 || error51.status === 403) {
69164
- return `Error performing web search: Perplexity search is not authorized (HTTP ${error51.status}${statusText}). Check the Perplexity API key or account access.`;
69165
- }
69166
- return `Error performing web search: Perplexity search failed (HTTP ${error51.status}${statusText}).`;
69294
+ import_node_fs6 = require("node:fs");
69295
+ import_node_path5 = __toESM(require("node:path"));
69296
+ UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36";
69297
+ SEARCH_TIMEOUT_MS = 15e3;
69298
+ READ_TIMEOUT_MS = 25e3;
69299
+ MAX_RAW_BYTES = 15e5;
69300
+ OPEN_URL_MAX_CHARS = 8e3;
69301
+ SEARCH_MAX_RESULTS = 30;
69302
+ HARD_PAGE_CEILING = 500;
69303
+ TIME_TO_DDG_DF = {
69304
+ past_day: "d",
69305
+ past_week: "w",
69306
+ past_month: "m",
69307
+ past_year: "y"
69308
+ };
69309
+ NAMED_ENTITIES = {
69310
+ amp: "&",
69311
+ lt: "<",
69312
+ gt: ">",
69313
+ quot: '"',
69314
+ apos: "'",
69315
+ nbsp: " ",
69316
+ "#39": "'"
69167
69317
  };
69168
- fetchPerplexitySearch = async (searchBody, abortSignal) => {
69169
- for (let attemptIndex = 0; attemptIndex < WEB_SEARCH_MAX_ATTEMPTS; attemptIndex++) {
69170
- const attempt = attemptIndex + 1;
69171
- const isFinalAttempt = attempt === WEB_SEARCH_MAX_ATTEMPTS;
69318
+ truncate3 = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
69319
+ isAbort = (err) => err instanceof Error && err.name === "AbortError";
69320
+ isTextual = (ct) => !ct || /text|html|json|xml|javascript|csv/i.test(ct);
69321
+ TIME_ENUM = external_exports.enum(["all", "past_day", "past_week", "past_month", "past_year"]).optional();
69322
+ createWebSearch = (_context) => tool({
69323
+ description: `Search the web for free (no API key required, DuckDuckGo-backed). Returns ranked results (title, url, snippet).
69324
+
69325
+ <instructions>
69326
+ - Give 1\u20135 \`queries\` that are VARIANTS of the SAME intent (query expansions), not different goals. For non-English topics add one English variant.
69327
+ - Break a complex need into step-by-step searches instead of one giant query.
69328
+ - You CAN use operators: site:, filetype:, inurl:, intitle:.
69329
+ - After searching, OPEN the most relevant results with open_url to actually read them; cross-check facts across 2+ sources.
69330
+ - If results are weak, refine the wording and search again.
69331
+ </instructions>`,
69332
+ inputSchema: external_exports.object({
69333
+ brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this search."),
69334
+ queries: external_exports.array(external_exports.string().trim().min(1).max(2e3)).min(1).max(5).describe("1\u20135 query variants of the same intent."),
69335
+ time: TIME_ENUM.describe(
69336
+ "Optional recency filter (past_day/week/month/year)."
69337
+ )
69338
+ }),
69339
+ execute: async ({ queries, time: time3 }, { abortSignal } = {}) => {
69340
+ const norm = Array.from(
69341
+ new Set(queries.map((q) => q.trim()).filter(Boolean))
69342
+ ).slice(0, 5);
69343
+ if (!norm.length) return "Erro: forne\xE7a ao menos uma query n\xE3o vazia.";
69172
69344
  try {
69173
- const response = await fetch(PERPLEXITY_SEARCH_URL, {
69174
- method: "POST",
69175
- headers: {
69176
- "Content-Type": "application/json",
69177
- Authorization: `Bearer ${process.env.PERPLEXITY_API_KEY || ""}`
69178
- },
69179
- body: JSON.stringify(searchBody),
69180
- signal: abortSignal
69181
- });
69182
- if (response.ok) {
69183
- return response;
69184
- }
69185
- const error51 = await createPerplexityApiError(response);
69186
- if (!error51.retryable || isFinalAttempt) {
69187
- throw error51;
69188
- }
69189
- const delayMs = getRetryDelayMs(attemptIndex);
69190
- console.warn("Web search provider error; retrying", {
69191
- attempt,
69192
- maxAttempts: WEB_SEARCH_MAX_ATTEMPTS,
69193
- status: error51.status,
69194
- statusText: error51.statusText,
69195
- bodySummary: error51.bodySummary,
69196
- delayMs
69197
- });
69198
- await sleep2(delayMs, abortSignal);
69199
- } catch (error51) {
69200
- if (error51 instanceof Error && error51.name === "AbortError") {
69201
- throw error51;
69202
- }
69203
- if (error51 instanceof PerplexityApiError) {
69204
- throw error51;
69205
- }
69206
- if (isFinalAttempt) {
69207
- throw error51;
69208
- }
69209
- const delayMs = getRetryDelayMs(attemptIndex);
69210
- console.warn("Web search network error; retrying", {
69211
- attempt,
69212
- maxAttempts: WEB_SEARCH_MAX_ATTEMPTS,
69213
- error: stringifyRedactedError(error51),
69214
- delayMs
69215
- });
69216
- await sleep2(delayMs, abortSignal);
69345
+ const df = time3 && time3 !== "all" ? TIME_TO_DDG_DF[time3] : void 0;
69346
+ const results = await searchQueries(norm, abortSignal, df);
69347
+ return formatSearchResults(results);
69348
+ } catch (err) {
69349
+ if (isAbort(err)) return "Erro: opera\xE7\xE3o abortada.";
69350
+ return `Erro na busca: ${err instanceof Error ? err.message : String(err)}`;
69217
69351
  }
69218
69352
  }
69219
- throw new Error("Web search failed before any Perplexity response was read");
69220
- };
69221
- normalizeSearchQueries = (rawQueries) => {
69222
- const queries = rawQueries.map((query) => query.trim()).filter(Boolean);
69223
- if (queries.length === 0) {
69224
- return { queries, error: EMPTY_QUERY_TOOL_ERROR };
69225
- }
69226
- if (queries.some((query) => query.length > PERPLEXITY_QUERY_MAX_LENGTH)) {
69227
- return { queries, error: QUERY_TOO_LONG_TOOL_ERROR };
69228
- }
69229
- return { queries: queries.slice(0, 3) };
69230
- };
69231
- createWebSearch = (context2) => {
69232
- const { userLocation, onToolCost } = context2;
69233
- return tool({
69234
- description: `Search for information across various sources.
69353
+ });
69354
+ createOpenUrl = (_opts) => tool({
69355
+ description: `Open a specific URL and return its readable text content (free, no API key). Use it to actually READ a link \u2014 the one the user pasted, or one from a prior search \u2014 before you act on it.
69235
69356
 
69236
69357
  <instructions>
69237
- - MUST use this tool to access up-to-date or external information when needed; DO NOT rely solely on internal knowledge
69238
- - Each search MUST contain exactly 1 to 3 \`queries\` (NEVER more than 3). Queries MUST be variants of the same intent (i.e., query expansions), NOT different goals
69239
- - For non-English queries, MUST include at least one English query as the final variant to expand coverage
69240
- - For complex searches, MUST break down into step-by-step searches instead of using a single complex query
69241
- - Access multiple URLs from search results for comprehensive information or cross-validation
69242
- - CAN use Google dork syntax (site:, filetype:, inurl:, intitle:, etc.) for targeted reconnaissance and pentest enumeration
69243
- - Only use \`time\` parameter when explicitly required by task, otherwise leave time range unrestricted
69244
- - Prioritize cybersecurity-relevant information: CVEs, CVSS scores, exploits, PoCs, security tools, and pentest methodologies
69245
- - Include specific versions, configurations, and technical details; cite reliable sources (NIST, OWASP, CVE databases)
69246
- - For commands/installations, prioritize Kali Linux compatibility using apt or pre-installed tools
69358
+ - The URL must be public and http(s).
69359
+ - Returns the page title + extracted text (JS is not executed; for JS-heavy pages use the recon toolkit's --browser path).
69360
+ - Read carefully, then verify important facts against another source.
69247
69361
  </instructions>`,
69248
- inputSchema: external_exports.object({
69249
- queries: external_exports.array(webSearchQuerySchema).min(1).max(3).describe(
69250
- "MAXIMUM 3 non-empty query variants (1-3 items only). Express the same search intent with different wording."
69251
- ),
69252
- time: external_exports.enum(["all", "past_day", "past_week", "past_month", "past_year"]).optional().describe(
69253
- "Optional time filter to limit results to a recent time range"
69254
- ),
69255
- brief: external_exports.string().describe(
69256
- "A one-sentence preamble describing the purpose of this operation"
69257
- )
69258
- }),
69259
- execute: async ({
69260
- queries: rawQueries,
69261
- time: time3
69262
- }, { abortSignal }) => {
69263
- try {
69264
- const { queries, error: error51 } = normalizeSearchQueries(rawQueries);
69265
- if (error51) {
69266
- return error51;
69267
- }
69268
- const searchBody = buildPerplexitySearchBody(
69269
- queries.length === 1 ? queries[0] : queries,
69270
- {
69271
- country: userLocation?.country,
69272
- recency: time3 && time3 !== "all" ? RECENCY_MAP[time3] : void 0
69273
- }
69274
- );
69275
- const response = await fetchPerplexitySearch(searchBody, abortSignal);
69276
- onToolCost?.(WEB_SEARCH_COST_PER_REQUEST);
69277
- const searchResponse = await response.json();
69278
- const isMultiQuery = queries.length > 1;
69279
- let allResults;
69280
- if (isMultiQuery && Array.isArray(searchResponse.results[0])) {
69281
- allResults = searchResponse.results.flat();
69282
- } else {
69283
- allResults = searchResponse.results;
69284
- }
69285
- return formatSearchResults(allResults);
69286
- } catch (error51) {
69287
- if (error51 instanceof Error && error51.name === "AbortError") {
69288
- return "Error: Operation aborted";
69289
- }
69290
- if (error51 instanceof PerplexityApiError) {
69291
- console.error("Web search tool error:", {
69292
- name: error51.name,
69293
- status: error51.status,
69294
- statusText: error51.statusText,
69295
- retryable: error51.retryable,
69296
- bodySummary: error51.bodySummary
69297
- });
69298
- return formatPerplexityFailureForTool(
69299
- error51,
69300
- error51.retryable ? WEB_SEARCH_MAX_ATTEMPTS : 1
69301
- );
69302
- }
69303
- const errorMessage = stringifyRedactedError(error51);
69304
- console.error("Web search tool error:", errorMessage);
69305
- return `Error performing web search: ${errorMessage}`;
69306
- }
69362
+ inputSchema: external_exports.object({
69363
+ brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this fetch."),
69364
+ url: external_exports.string().describe("The http(s) URL to open and read.")
69365
+ }),
69366
+ execute: async ({ url: url2 }, { abortSignal } = {}) => {
69367
+ const canon = canonicalUrl(url2);
69368
+ if (!canon) return `URL inv\xE1lida: ${url2}`;
69369
+ try {
69370
+ const jina = await jinaRead(canon, abortSignal);
69371
+ if (jina) return truncate3(jina, OPEN_URL_MAX_CHARS * 2);
69372
+ const page = await readPage(canon, abortSignal);
69373
+ if (!isTextual(page.contentType)) {
69374
+ return `(${page.finalUrl}) conte\xFAdo n\xE3o textual (${page.contentType}, ${page.bytes} bytes) \u2014 n\xE3o extra\xEDdo.`;
69375
+ }
69376
+ const head = `# ${page.title}
69377
+ ${page.finalUrl} (HTTP ${page.status}, ${page.bytes} bytes)
69378
+ `;
69379
+ const body = page.text || "(sem texto extra\xEDvel \u2014 pode ser JS puro; use a recon toolkit com --browser)";
69380
+ return head + "\n" + truncate3(body, OPEN_URL_MAX_CHARS);
69381
+ } catch (err) {
69382
+ if (isAbort(err)) return "Erro: opera\xE7\xE3o abortada.";
69383
+ return `Erro ao abrir URL: ${err instanceof Error ? err.message : String(err)}`;
69307
69384
  }
69308
- });
69309
- };
69310
- }
69311
- });
69312
-
69313
- // ../lib/ai/tools/open-url.ts
69314
- var createOpenUrlTool;
69315
- var init_open_url = __esm({
69316
- "../lib/ai/tools/open-url.ts"() {
69317
- "use strict";
69318
- init_dist5();
69319
- init_zod();
69320
- init_token_utils();
69321
- createOpenUrlTool = () => {
69322
- return tool({
69323
- description: `Retrieve the full contents of a specific webpage by URL.
69385
+ }
69386
+ });
69387
+ createDeepResearch = (opts) => tool({
69388
+ description: `Deep web research: search + fetch MANY pages concurrently and return a synthesized digest with sources. Free, no API key.
69324
69389
 
69325
69390
  <instructions>
69326
- - Use to fetch and read a specific webpage, usually obtained from a prior search
69327
- - URLs must be valid and publicly accessible
69328
- - Prioritize cybersecurity-relevant information: CVEs, CVSS scores, exploits, PoCs, security tools, and pentest methodologies
69329
- - Include specific versions, configurations, and technical details; cite reliable sources (NIST, OWASP, CVE databases)
69391
+ - Provide \`queries\` (1\u20136 variants) and/or seed \`urls\`. It searches, collects candidate links, and fetches up to \`max_pages\` of them in parallel.
69392
+ - Tune to the SUBJECT: a quick fact needs a handful of pages; a broad survey can sweep up to 500. Do NOT fetch 500 for something small.
69393
+ - Returns: per-source title + url + a short extract, plus counts. A full Markdown report is saved under the workspace's reports/ when possible.
69394
+ - Use this for broad/unknown topics and cross-validation; use open_url to drill into a single page, web_search for a quick lookup.
69330
69395
  </instructions>`,
69331
- inputSchema: external_exports.object({
69332
- url: external_exports.string().describe("The URL to open and retrieve content from"),
69333
- brief: external_exports.string().describe(
69334
- "A one-sentence preamble describing the purpose of this operation"
69335
- )
69336
- }),
69337
- execute: async ({ url: url2 }, { abortSignal }) => {
69338
- try {
69339
- const jinaUrl = `https://r.jina.ai/${encodeURIComponent(url2)}`;
69340
- const response = await fetch(jinaUrl, {
69341
- method: "GET",
69342
- headers: {
69343
- Authorization: `Bearer ${process.env.JINA_API_KEY}`,
69344
- "X-Timeout": "30",
69345
- "X-Base": "final",
69346
- "X-Token-Budget": "200000"
69347
- },
69348
- signal: abortSignal
69349
- });
69350
- if (!response.ok) {
69351
- const errorBody = await response.text();
69352
- return `Error: HTTP ${response.status} - ${errorBody}`;
69353
- }
69354
- const content = await response.text();
69355
- const truncated = truncateContent(content, void 0, 2048);
69356
- return truncated;
69357
- } catch (error51) {
69358
- if (error51 instanceof Error && error51.name === "AbortError") {
69359
- return "Error: Operation aborted";
69360
- }
69361
- console.error("Open URL tool error:", error51);
69362
- const errorMessage = error51 instanceof Error ? error51.message : "Unknown error occurred";
69363
- return `Error opening URL: ${errorMessage}`;
69364
- }
69396
+ inputSchema: external_exports.object({
69397
+ brief: external_exports.string().describe("A one-sentence preamble describing the research goal."),
69398
+ queries: external_exports.array(external_exports.string().trim().min(1).max(2e3)).max(6).optional().describe("1\u20136 search query variants of the same intent."),
69399
+ urls: external_exports.array(external_exports.string()).max(HARD_PAGE_CEILING).optional().describe("Optional seed URLs to include directly."),
69400
+ max_pages: external_exports.number().int().min(1).max(HARD_PAGE_CEILING).optional().describe("Max pages to fetch (default 12, ceiling 500). Match to the subject."),
69401
+ concurrency: external_exports.number().int().min(1).max(HARD_PAGE_CEILING).optional().describe("Parallel fetches (default 8, up to 500)."),
69402
+ time: TIME_ENUM.describe("Optional recency filter for the search step.")
69403
+ }),
69404
+ execute: async (input, { abortSignal } = {}) => {
69405
+ const queries = Array.from(
69406
+ new Set((input.queries || []).map((q) => q.trim()).filter(Boolean))
69407
+ ).slice(0, 6);
69408
+ const seeds = [];
69409
+ for (const u of input.urls || []) {
69410
+ const c = canonicalUrl(u);
69411
+ if (c) seeds.push({ title: c, url: c, snippet: "" });
69412
+ }
69413
+ if (!queries.length && !seeds.length) {
69414
+ return "Erro: forne\xE7a queries e/ou urls para pesquisar.";
69415
+ }
69416
+ const maxPages = Math.min(
69417
+ Math.max(1, input.max_pages ?? 12),
69418
+ HARD_PAGE_CEILING
69419
+ );
69420
+ const requestedConc = Math.min(
69421
+ Math.max(1, input.concurrency ?? 8),
69422
+ HARD_PAGE_CEILING
69423
+ );
69424
+ try {
69425
+ const df = input.time && input.time !== "all" ? TIME_TO_DDG_DF[input.time] : void 0;
69426
+ const searched = queries.length ? await searchQueries(queries, abortSignal, df) : [];
69427
+ const candidates = dedupeResults([...seeds, ...searched]);
69428
+ const totalFound = candidates.length;
69429
+ const capped = totalFound > maxPages;
69430
+ const toFetch = candidates.slice(0, maxPages);
69431
+ const conc = Math.min(requestedConc, toFetch.length || 1);
69432
+ const fetched = await mapPool(
69433
+ toFetch,
69434
+ conc,
69435
+ (r2) => readPage(r2.url, abortSignal)
69436
+ );
69437
+ const pages = [];
69438
+ let failures = 0;
69439
+ fetched.forEach((p) => {
69440
+ if (p && "error" in p) failures++;
69441
+ else if (p) pages.push(p);
69442
+ });
69443
+ const reportPath = await saveResearchReport(
69444
+ opts.workdir,
69445
+ queries,
69446
+ pages,
69447
+ { totalFound, fetched: toFetch.length, ok: pages.length, failures, capped, maxPages }
69448
+ );
69449
+ return formatResearchDigest(pages, {
69450
+ totalFound,
69451
+ fetched: toFetch.length,
69452
+ ok: pages.length,
69453
+ failures,
69454
+ capped,
69455
+ maxPages,
69456
+ conc,
69457
+ reportPath
69458
+ });
69459
+ } catch (err) {
69460
+ if (isAbort(err)) return "Erro: opera\xE7\xE3o abortada.";
69461
+ return `Erro na pesquisa profunda: ${err instanceof Error ? err.message : String(err)}`;
69365
69462
  }
69366
- });
69367
- };
69463
+ }
69464
+ });
69368
69465
  }
69369
69466
  });
69370
69467
 
69371
69468
  // src/tools/scope.ts
69372
- var import_node_fs6, import_node_path5, import_promises, ipv4ToInt, isIpLiteral, maskForPrefix, parseCidr, ScopeImpl, scopeFileFor, loadScope, hostFromUrl;
69469
+ var import_node_fs7, import_node_path6, import_promises, ipv4ToInt, isIpLiteral, maskForPrefix, parseCidr, ScopeImpl, scopeFileFor, loadScope, hostFromUrl;
69373
69470
  var init_scope = __esm({
69374
69471
  "src/tools/scope.ts"() {
69375
69472
  "use strict";
69376
- import_node_fs6 = __toESM(require("node:fs"));
69377
- import_node_path5 = __toESM(require("node:path"));
69473
+ import_node_fs7 = __toESM(require("node:fs"));
69474
+ import_node_path6 = __toESM(require("node:path"));
69378
69475
  import_promises = __toESM(require("node:dns/promises"));
69379
69476
  ipv4ToInt = (ip) => {
69380
69477
  const parts = ip.split(".");
@@ -69472,11 +69569,11 @@ var init_scope = __esm({
69472
69569
  return false;
69473
69570
  }
69474
69571
  };
69475
- scopeFileFor = (workdir) => import_node_path5.default.join(workdir, "recon", "scope.txt");
69572
+ scopeFileFor = (workdir) => import_node_path6.default.join(workdir, "recon", "scope.txt");
69476
69573
  loadScope = (workdir) => {
69477
69574
  const file2 = scopeFileFor(workdir);
69478
69575
  try {
69479
- const text2 = import_node_fs6.default.readFileSync(file2, "utf8");
69576
+ const text2 = import_node_fs7.default.readFileSync(file2, "utf8");
69480
69577
  return ScopeImpl.parse(text2, file2);
69481
69578
  } catch {
69482
69579
  return ScopeImpl.parse("", null);
@@ -69493,7 +69590,7 @@ var init_scope = __esm({
69493
69590
  });
69494
69591
 
69495
69592
  // src/tools/http-request.ts
69496
- var FUZZ, MAX_FUZZ, BODY_PREVIEW_CHARS, DEFAULT_TIMEOUT_S, LOOPBACK, numberEnv, sleep3, sampleUrl, NOTABLE_HEADERS, applyFuzz, buildInit, collectNotableHeaders, doRequest, summarizeFuzz, scopeRefusal, createHttpRequest;
69593
+ var FUZZ, MAX_FUZZ, BODY_PREVIEW_CHARS, DEFAULT_TIMEOUT_S, LOOPBACK, numberEnv, sleep2, sampleUrl, NOTABLE_HEADERS, applyFuzz, buildInit, collectNotableHeaders, doRequest, summarizeFuzz, scopeRefusal, createHttpRequest;
69497
69594
  var init_http_request = __esm({
69498
69595
  "src/tools/http-request.ts"() {
69499
69596
  "use strict";
@@ -69509,7 +69606,7 @@ var init_http_request = __esm({
69509
69606
  const raw = Number(process.env[name25]);
69510
69607
  return Number.isFinite(raw) && raw >= 0 ? raw : void 0;
69511
69608
  };
69512
- sleep3 = (ms, signal) => new Promise((resolve2) => {
69609
+ sleep2 = (ms, signal) => new Promise((resolve2) => {
69513
69610
  if (ms <= 0 || signal?.aborted) return resolve2();
69514
69611
  const t = setTimeout(resolve2, ms);
69515
69612
  signal?.addEventListener(
@@ -69630,7 +69727,7 @@ var init_http_request = __esm({
69630
69727
  };
69631
69728
  scopeRefusal = (host, workdir) => `Recusado: o host "${host}" n\xE3o est\xE1 no escopo autorizado. Adicione-o a ${scopeFileFor(workdir)} (formatos: example.com, *.example.com, CIDR 10.0.0.0/24, ou um IP) \u2014 somente alvos que voc\xEA possui ou tem autoriza\xE7\xE3o expl\xEDcita para testar. Edite o scope.txt com a ferramenta file e tente de novo.`;
69632
69729
  createHttpRequest = (deps) => {
69633
- const { workdir, loadScopeFn = loadScope } = deps;
69730
+ const { workdir, loadScopeFn = loadScope, unrestricted = false } = deps;
69634
69731
  const envThrottle = numberEnv("clawfast_HTTP_THROTTLE_MS");
69635
69732
  const envJitter = numberEnv("clawfast_HTTP_JITTER_MS");
69636
69733
  return tool({
@@ -69644,20 +69741,30 @@ SCOPE: every request is gated by recon/scope.txt (same allowlist as the recon to
69644
69741
 
69645
69742
  USE FOR: testing a single endpoint, replaying/tampering a captured request, parameter/path/value fuzzing, verifying an exploit by observing the real response. NOT a replacement for bulk crawling \u2014 use the Python recon toolkit for that.`,
69646
69743
  inputSchema: external_exports.object({
69647
- brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this request."),
69744
+ brief: external_exports.string().describe(
69745
+ "A one-sentence preamble describing the purpose of this request."
69746
+ ),
69648
69747
  url: external_exports.string().url().describe(
69649
69748
  "Target URL. May contain the marker FUZZ (replaced by each fuzz payload)."
69650
69749
  ),
69651
69750
  method: external_exports.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).optional().describe("HTTP method (default GET)."),
69652
69751
  headers: external_exports.record(external_exports.string(), external_exports.string()).optional().describe("Request headers. Values may contain FUZZ."),
69653
69752
  body: external_exports.string().optional().describe("Request body for POST/PUT/PATCH/DELETE. May contain FUZZ."),
69654
- follow_redirects: external_exports.boolean().optional().describe("Follow 3xx redirects (default true). Set false to inspect Location."),
69655
- timeout: external_exports.number().optional().describe(`Per-request timeout in seconds (default ${DEFAULT_TIMEOUT_S}).`),
69753
+ follow_redirects: external_exports.boolean().optional().describe(
69754
+ "Follow 3xx redirects (default true). Set false to inspect Location."
69755
+ ),
69756
+ timeout: external_exports.number().optional().describe(
69757
+ `Per-request timeout in seconds (default ${DEFAULT_TIMEOUT_S}).`
69758
+ ),
69656
69759
  fuzz: external_exports.array(external_exports.string()).optional().describe(
69657
69760
  `Intruder payloads \u2014 substituted into the FUZZ marker, one request each (max ${MAX_FUZZ}).`
69658
69761
  ),
69659
- throttle_ms: external_exports.number().optional().describe("OPSEC: delay between fuzz requests in ms (overrides clawfast_HTTP_THROTTLE_MS)."),
69660
- jitter_ms: external_exports.number().optional().describe("OPSEC: random extra delay 0..jitter added to each throttle.")
69762
+ throttle_ms: external_exports.number().optional().describe(
69763
+ "OPSEC: delay between fuzz requests in ms (overrides clawfast_HTTP_THROTTLE_MS)."
69764
+ ),
69765
+ jitter_ms: external_exports.number().optional().describe(
69766
+ "OPSEC: random extra delay 0..jitter added to each throttle."
69767
+ )
69661
69768
  }),
69662
69769
  execute: async (input, { abortSignal } = {}) => {
69663
69770
  const method = input.method ?? "GET";
@@ -69667,7 +69774,7 @@ USE FOR: testing a single endpoint, replaying/tampering a captured request, para
69667
69774
  if (!host) {
69668
69775
  return { error: `URL inv\xE1lida: ${input.url}` };
69669
69776
  }
69670
- if (!LOOPBACK.has(host)) {
69777
+ if (!unrestricted && !LOOPBACK.has(host)) {
69671
69778
  const scope = loadScopeFn(workdir);
69672
69779
  const allowed = await scope.allows(host);
69673
69780
  if (!allowed) {
@@ -69707,7 +69814,13 @@ USE FOR: testing a single endpoint, replaying/tampering a captured request, para
69707
69814
  abortSignal
69708
69815
  );
69709
69816
  if ("error" in res2) {
69710
- rows.push({ payload, status: "ERR", bodyLength: 0, timeMs: res2.timeMs, error: res2.error });
69817
+ rows.push({
69818
+ payload,
69819
+ status: "ERR",
69820
+ bodyLength: 0,
69821
+ timeMs: res2.timeMs,
69822
+ error: res2.error
69823
+ });
69711
69824
  } else {
69712
69825
  rows.push({
69713
69826
  payload,
@@ -69718,7 +69831,10 @@ USE FOR: testing a single endpoint, replaying/tampering a captured request, para
69718
69831
  });
69719
69832
  }
69720
69833
  if (i < list.length - 1 && (throttle > 0 || jitter > 0)) {
69721
- await sleep3(throttle + Math.floor(Math.random() * (jitter + 1)), abortSignal);
69834
+ await sleep2(
69835
+ throttle + Math.floor(Math.random() * (jitter + 1)),
69836
+ abortSignal
69837
+ );
69722
69838
  }
69723
69839
  }
69724
69840
  return { fuzz: { host, count: rows.length, rows } };
@@ -69796,12 +69912,12 @@ ${s.bodyPreview}`
69796
69912
  });
69797
69913
 
69798
69914
  // src/tools/findings.ts
69799
- var import_node_fs7, import_node_path6, SEVERITIES, STATUSES, SEVERITY_RANK, SEVERITY_EMOJI, findingsStoreFor, readAll, writeAll, nextId, sortBySeverity, renderReport, oneLine, createFindings;
69915
+ var import_node_fs8, import_node_path7, SEVERITIES, STATUSES, SEVERITY_RANK, SEVERITY_EMOJI, findingsStoreFor, readAll, writeAll, nextId, sortBySeverity, renderReport, oneLine, createFindings;
69800
69916
  var init_findings = __esm({
69801
69917
  "src/tools/findings.ts"() {
69802
69918
  "use strict";
69803
- import_node_fs7 = __toESM(require("node:fs"));
69804
- import_node_path6 = __toESM(require("node:path"));
69919
+ import_node_fs8 = __toESM(require("node:fs"));
69920
+ import_node_path7 = __toESM(require("node:path"));
69805
69921
  init_dist5();
69806
69922
  init_zod();
69807
69923
  SEVERITIES = [
@@ -69827,16 +69943,16 @@ var init_findings = __esm({
69827
69943
  info: "\u26AA"
69828
69944
  };
69829
69945
  findingsStoreFor = (workdir) => {
69830
- const findingsDir = import_node_path6.default.join(workdir, "findings");
69946
+ const findingsDir = import_node_path7.default.join(workdir, "findings");
69831
69947
  return {
69832
69948
  findingsDir,
69833
- storeFile: import_node_path6.default.join(findingsDir, "findings.json"),
69834
- reportFile: import_node_path6.default.join(findingsDir, "report.md")
69949
+ storeFile: import_node_path7.default.join(findingsDir, "findings.json"),
69950
+ reportFile: import_node_path7.default.join(findingsDir, "report.md")
69835
69951
  };
69836
69952
  };
69837
69953
  readAll = (store) => {
69838
69954
  try {
69839
- const raw = import_node_fs7.default.readFileSync(store.storeFile, "utf8");
69955
+ const raw = import_node_fs8.default.readFileSync(store.storeFile, "utf8");
69840
69956
  const parsed = JSON.parse(raw);
69841
69957
  return Array.isArray(parsed) ? parsed : [];
69842
69958
  } catch {
@@ -69844,8 +69960,8 @@ var init_findings = __esm({
69844
69960
  }
69845
69961
  };
69846
69962
  writeAll = (store, findings) => {
69847
- import_node_fs7.default.mkdirSync(store.findingsDir, { recursive: true });
69848
- import_node_fs7.default.writeFileSync(store.storeFile, JSON.stringify(findings, null, 2), "utf8");
69963
+ import_node_fs8.default.mkdirSync(store.findingsDir, { recursive: true });
69964
+ import_node_fs8.default.writeFileSync(store.storeFile, JSON.stringify(findings, null, 2), "utf8");
69849
69965
  };
69850
69966
  nextId = (findings) => {
69851
69967
  let max = 0;
@@ -69944,7 +70060,9 @@ ACTIONS
69944
70060
  Use this together with http_request: probe \u2192 observe \u2192 record evidence \u2192 mark confirmed.`,
69945
70061
  inputSchema: external_exports.object({
69946
70062
  action: external_exports.enum(["add", "update", "list", "report"]),
69947
- brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this operation."),
70063
+ brief: external_exports.string().describe(
70064
+ "A one-sentence preamble describing the purpose of this operation."
70065
+ ),
69948
70066
  id: external_exports.string().optional().describe("Finding id (e.g. F-001) \u2014 required for update."),
69949
70067
  title: external_exports.string().optional(),
69950
70068
  severity: external_exports.enum(SEVERITIES).optional(),
@@ -70017,7 +70135,9 @@ Use this together with http_request: probe \u2192 observe \u2192 record evidence
70017
70135
  if (input.action === "list") {
70018
70136
  let filtered = findings;
70019
70137
  if (input.filter_severity) {
70020
- filtered = filtered.filter((f) => f.severity === input.filter_severity);
70138
+ filtered = filtered.filter(
70139
+ (f) => f.severity === input.filter_severity
70140
+ );
70021
70141
  }
70022
70142
  if (input.filter_status) {
70023
70143
  filtered = filtered.filter((f) => f.status === input.filter_status);
@@ -70029,8 +70149,8 @@ Use this together with http_request: probe \u2192 observe \u2192 record evidence
70029
70149
  };
70030
70150
  }
70031
70151
  const md = renderReport(findings);
70032
- import_node_fs7.default.mkdirSync(store.findingsDir, { recursive: true });
70033
- import_node_fs7.default.writeFileSync(store.reportFile, md, "utf8");
70152
+ import_node_fs8.default.mkdirSync(store.findingsDir, { recursive: true });
70153
+ import_node_fs8.default.writeFileSync(store.reportFile, md, "utf8");
70034
70154
  return { report: store.reportFile, total: findings.length };
70035
70155
  } catch (err) {
70036
70156
  return {
@@ -70260,20 +70380,48 @@ var init_utils4 = __esm({
70260
70380
 
70261
70381
  // src/local-sandbox.ts
70262
70382
  function inferShellFlag(shell2) {
70263
- const base = import_node_path7.default.basename(shell2).toLowerCase();
70383
+ const base = import_node_path8.default.basename(shell2).toLowerCase();
70264
70384
  if (base === "cmd" || base === "cmd.exe") return "/C";
70265
70385
  if (base === "powershell" || base === "powershell.exe" || base === "pwsh") {
70266
70386
  return "-Command";
70267
70387
  }
70268
70388
  return "-c";
70269
70389
  }
70270
- var import_node_child_process2, import_node_fs8, import_node_path7, import_node_os3, import_node_url, import_meta, LocalSandbox;
70390
+ function detectLinux(platform) {
70391
+ if (platform !== "linux") {
70392
+ return { distroId: "", distroName: "", isKali: false };
70393
+ }
70394
+ try {
70395
+ const text2 = (0, import_node_fs9.readFileSync)("/etc/os-release", "utf8");
70396
+ const map2 = {};
70397
+ for (const raw of text2.split(/\r?\n/)) {
70398
+ const eq = raw.indexOf("=");
70399
+ if (eq === -1) continue;
70400
+ const key = raw.slice(0, eq).trim();
70401
+ const val = raw.slice(eq + 1).trim().replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
70402
+ if (key) map2[key] = val;
70403
+ }
70404
+ const id = (map2.ID || "").toLowerCase();
70405
+ const idLike = (map2.ID_LIKE || "").toLowerCase();
70406
+ const distroName = map2.PRETTY_NAME || map2.NAME || "Linux";
70407
+ const isKali = id === "kali" || idLike.includes("kali") || /kali/i.test(distroName);
70408
+ return { distroId: id, distroName, isKali };
70409
+ } catch {
70410
+ return { distroId: "", distroName: "Linux", isKali: false };
70411
+ }
70412
+ }
70413
+ function computeUnrestricted(platform) {
70414
+ if (truthyEnv("CLAWFAST_UNRESTRICTED")) return true;
70415
+ if (truthyEnv("CLAWFAST_STRICT_SCOPE")) return false;
70416
+ return platform === "linux";
70417
+ }
70418
+ var import_node_child_process2, import_node_fs9, import_node_path8, import_node_os3, import_node_url, import_meta, LocalSandbox, truthyEnv;
70271
70419
  var init_local_sandbox = __esm({
70272
70420
  "src/local-sandbox.ts"() {
70273
70421
  "use strict";
70274
70422
  import_node_child_process2 = require("node:child_process");
70275
- import_node_fs8 = require("node:fs");
70276
- import_node_path7 = __toESM(require("node:path"));
70423
+ import_node_fs9 = require("node:fs");
70424
+ import_node_path8 = __toESM(require("node:path"));
70277
70425
  import_node_os3 = __toESM(require("node:os"));
70278
70426
  import_node_url = require("node:url");
70279
70427
  init_utils4();
@@ -70285,6 +70433,10 @@ var init_local_sandbox = __esm({
70285
70433
  // The most recent foreground child. Lets the CLI forward user-typed lines to
70286
70434
  // a command that is waiting for stdin (interactive prompts, REPLs, etc.).
70287
70435
  this.activeForegroundChild = null;
70436
+ // Host fingerprint, computed once at construction. Linux/Kali unlock the
70437
+ // "full-power" sandbox context; root status flips sudo guidance; unrestricted
70438
+ // opens the native http_request / recon scope gate (default ON for Linux).
70439
+ this.platform = import_node_os3.default.platform();
70288
70440
  this.commands = {
70289
70441
  run: (command, opts) => {
70290
70442
  return new Promise((resolve2, reject) => {
@@ -70371,15 +70523,15 @@ var init_local_sandbox = __esm({
70371
70523
  this.files = {
70372
70524
  write: async (filePath, content) => {
70373
70525
  const resolved = this.resolvePath(filePath);
70374
- await import_node_fs8.promises.mkdir(import_node_path7.default.dirname(resolved), { recursive: true });
70526
+ await import_node_fs9.promises.mkdir(import_node_path8.default.dirname(resolved), { recursive: true });
70375
70527
  const data = typeof content === "string" ? content : content instanceof ArrayBuffer ? Buffer.from(content) : content;
70376
- await import_node_fs8.promises.writeFile(resolved, data);
70528
+ await import_node_fs9.promises.writeFile(resolved, data);
70377
70529
  },
70378
70530
  read: async (filePath) => {
70379
- return import_node_fs8.promises.readFile(this.resolvePath(filePath), "utf8");
70531
+ return import_node_fs9.promises.readFile(this.resolvePath(filePath), "utf8");
70380
70532
  },
70381
70533
  remove: async (filePath) => {
70382
- await import_node_fs8.promises.rm(this.resolvePath(filePath), {
70534
+ await import_node_fs9.promises.rm(this.resolvePath(filePath), {
70383
70535
  recursive: true,
70384
70536
  force: true
70385
70537
  });
@@ -70387,8 +70539,8 @@ var init_local_sandbox = __esm({
70387
70539
  list: async (dirPath = ".") => {
70388
70540
  const resolved = this.resolvePath(dirPath);
70389
70541
  try {
70390
- const entries = await import_node_fs8.promises.readdir(resolved, { withFileTypes: true });
70391
- return entries.filter((e) => e.isFile()).map((e) => ({ name: import_node_path7.default.join(resolved, e.name) }));
70542
+ const entries = await import_node_fs9.promises.readdir(resolved, { withFileTypes: true });
70543
+ return entries.filter((e) => e.isFile()).map((e) => ({ name: import_node_path8.default.join(resolved, e.name) }));
70392
70544
  } catch {
70393
70545
  return [];
70394
70546
  }
@@ -70404,12 +70556,15 @@ var init_local_sandbox = __esm({
70404
70556
  this.shellFlag = shell2.shellFlag;
70405
70557
  }
70406
70558
  const explicit = opts?.workdir || process.env.CLI_WORKDIR;
70407
- const base = explicit || import_node_path7.default.join(process.cwd(), "SPRIT");
70559
+ const base = explicit || import_node_path8.default.join(process.cwd(), "SPRIT");
70408
70560
  this.autoCreated = !explicit;
70409
- this.workdir = import_node_path7.default.resolve(base).replace(/\\/g, "/");
70561
+ this.workdir = import_node_path8.default.resolve(base).replace(/\\/g, "/");
70562
+ this.linux = detectLinux(this.platform);
70563
+ this.root = typeof process.getuid === "function" ? process.getuid() === 0 : false;
70564
+ this.unrestricted = computeUnrestricted(this.platform);
70410
70565
  }
70411
70566
  async init() {
70412
- await import_node_fs8.promises.mkdir(this.workdir, { recursive: true });
70567
+ await import_node_fs9.promises.mkdir(this.workdir, { recursive: true });
70413
70568
  await this.seedReconToolkit();
70414
70569
  await this.seedAuditToolkit();
70415
70570
  }
@@ -70426,8 +70581,8 @@ var init_local_sandbox = __esm({
70426
70581
  const assetsDir = (0, import_node_url.fileURLToPath)(
70427
70582
  new URL("../assets/recon", import_meta.url)
70428
70583
  );
70429
- const destDir = import_node_path7.default.join(this.workdir, "recon");
70430
- await import_node_fs8.promises.mkdir(destDir, { recursive: true });
70584
+ const destDir = import_node_path8.default.join(this.workdir, "recon");
70585
+ await import_node_fs9.promises.mkdir(destDir, { recursive: true });
70431
70586
  const files = [
70432
70587
  ["recon_deep.py", true],
70433
70588
  ["recon_common.py", true],
@@ -70443,17 +70598,17 @@ var init_local_sandbox = __esm({
70443
70598
  ["scope.txt", false]
70444
70599
  ];
70445
70600
  for (const [name25, overwrite] of files) {
70446
- const dest = import_node_path7.default.join(destDir, name25);
70601
+ const dest = import_node_path8.default.join(destDir, name25);
70447
70602
  if (!overwrite) {
70448
70603
  try {
70449
- await import_node_fs8.promises.access(dest);
70604
+ await import_node_fs9.promises.access(dest);
70450
70605
  continue;
70451
70606
  } catch {
70452
70607
  }
70453
70608
  }
70454
70609
  try {
70455
- const content = await import_node_fs8.promises.readFile(import_node_path7.default.join(assetsDir, name25));
70456
- await import_node_fs8.promises.writeFile(dest, content);
70610
+ const content = await import_node_fs9.promises.readFile(import_node_path8.default.join(assetsDir, name25));
70611
+ await import_node_fs9.promises.writeFile(dest, content);
70457
70612
  } catch {
70458
70613
  }
70459
70614
  }
@@ -70471,12 +70626,12 @@ var init_local_sandbox = __esm({
70471
70626
  const assetsDir = (0, import_node_url.fileURLToPath)(
70472
70627
  new URL("../assets/audit", import_meta.url)
70473
70628
  );
70474
- const destDir = import_node_path7.default.join(this.workdir, "audit");
70475
- await import_node_fs8.promises.mkdir(destDir, { recursive: true });
70629
+ const destDir = import_node_path8.default.join(this.workdir, "audit");
70630
+ await import_node_fs9.promises.mkdir(destDir, { recursive: true });
70476
70631
  for (const name25 of ["project_audit.py", "README.md"]) {
70477
70632
  try {
70478
- const content = await import_node_fs8.promises.readFile(import_node_path7.default.join(assetsDir, name25));
70479
- await import_node_fs8.promises.writeFile(import_node_path7.default.join(destDir, name25), content);
70633
+ const content = await import_node_fs9.promises.readFile(import_node_path8.default.join(assetsDir, name25));
70634
+ await import_node_fs9.promises.writeFile(import_node_path8.default.join(destDir, name25), content);
70480
70635
  } catch {
70481
70636
  }
70482
70637
  }
@@ -70495,6 +70650,30 @@ var init_local_sandbox = __esm({
70495
70650
  isWindows() {
70496
70651
  return import_node_os3.default.platform() === "win32" && !this.isBashLikeShell();
70497
70652
  }
70653
+ /** True on any Linux host — unlocks the full-power sandbox context. */
70654
+ isLinux() {
70655
+ return this.platform === "linux";
70656
+ }
70657
+ /** True when /etc/os-release identifies this host as Kali Linux. */
70658
+ isKali() {
70659
+ return this.linux.isKali;
70660
+ }
70661
+ /** Pretty distro name (PRETTY_NAME/NAME), e.g. "Kali GNU/Linux Rolling". */
70662
+ getDistroName() {
70663
+ return this.linux.distroName;
70664
+ }
70665
+ /** True when the session runs as uid 0 (no sudo needed). */
70666
+ isRoot() {
70667
+ return this.root;
70668
+ }
70669
+ /**
70670
+ * True when the native http_request tool and the recon toolkit run WITHOUT
70671
+ * the scope.txt allowlist gate. Defaults to ON for Linux ("tudo liberado"),
70672
+ * OFF elsewhere; flip with CLAWFAST_UNRESTRICTED / CLAWFAST_STRICT_SCOPE.
70673
+ */
70674
+ isUnrestricted() {
70675
+ return this.unrestricted;
70676
+ }
70498
70677
  supportsPty() {
70499
70678
  return false;
70500
70679
  }
@@ -70503,10 +70682,12 @@ var init_local_sandbox = __esm({
70503
70682
  const shellInvocation = `${this.shellBin} ${this.shellFlag}`;
70504
70683
  const windowsNotes = import_node_os3.default.platform() === "win32" ? this.getWindowsShellNotes() : "";
70505
70684
  const pentestToolingNotes = this.getLocalPentestToolingNotes();
70506
- return `You are executing commands directly on the user's local host (${platform}, hostname "${import_node_os3.default.hostname()}") in DANGEROUS MODE: there is NO sandbox isolation.
70685
+ const linuxPowerNotes = this.isLinux() ? this.getLinuxPowerNotes() : "";
70686
+ const hostLabel = this.isLinux() ? `${this.getDistroName()} ${import_node_os3.default.arch()}, ${this.root ? "root" : "user"}@${import_node_os3.default.hostname()}` : `${platform}, hostname "${import_node_os3.default.hostname()}"`;
70687
+ return `You are executing commands directly on the user's local host (${hostLabel}) in DANGEROUS MODE: there is NO sandbox isolation. This is the operator's OWN machine and they have authorized you to use its FULL power.
70507
70688
  Commands are invoked via \`${shellInvocation}\`. The working directory is "${this.workdir}".
70508
70689
  Be careful: file system, network and process operations all affect the real host system.
70509
- A real human user is present at this terminal. When a command prompts for input \u2014 a password, a y/n confirmation, an interactive installer, a REPL prompt (python, mysql, ftp), an ssh/sudo prompt, etc. \u2014 the user types the answer and it is forwarded to the command's stdin. So you MAY run commands that prompt for input; just run the command and the user will respond when asked. Prefer non-interactive flags (like --yes) when they exist and are convenient, but interactive prompts are fully supported. Only full-screen / raw-mode TUI programs (vim, top, htop, less without \`| cat\`) are unsupported, since there is no PTY \u2014 avoid those and use line-oriented alternatives.
70690
+ A real human user is present at this terminal. When a command prompts for input \u2014 a password, a y/n confirmation, an interactive installer, a REPL prompt (python, mysql, ftp), an ssh/sudo prompt, an account/registration step, etc. \u2014 the user types the answer and it is forwarded to the command's stdin. So you MAY run commands that prompt for input; just run the command and the user will respond when asked. Prefer non-interactive flags (like --yes) when they exist and are convenient, but interactive prompts are fully supported. Only full-screen / raw-mode TUI programs (vim, top, htop, less without \`| cat\`) are unsupported, since there is no PTY \u2014 avoid those and use line-oriented alternatives.
70510
70691
 
70511
70692
  WORKSPACE - STRICT SCOPE:
70512
70693
  - Your entire workspace is the directory: ${this.workdir}
@@ -70523,7 +70704,28 @@ EDITING SCRIPTS:
70523
70704
  - When a script you wrote produces an error, fix the specific broken part.
70524
70705
  - Do not recreate or fully overwrite a file just to fix one error.
70525
70706
  - Use write only to create a brand-new file, or when changes to a short file are extensive.
70526
- - Iterate on your own existing files across steps; treat them as persistent.${windowsNotes}${pentestToolingNotes}`;
70707
+ - Iterate on your own existing files across steps; treat them as persistent.${linuxPowerNotes}${windowsNotes}${pentestToolingNotes}`;
70708
+ }
70709
+ /**
70710
+ * The Linux "full-power" block: tells the model it is on a real Linux/Kali box
70711
+ * with no capability artificially withheld, how root/sudo works here, that
70712
+ * Python and scanning are fully unlocked, and — critically — the protocol for
70713
+ * relaying a Kali tool's interactive prompt to the human and forwarding their
70714
+ * typed answer back to the program's stdin.
70715
+ */
70716
+ getLinuxPowerNotes() {
70717
+ const who = this.root ? "root (uid 0) \u2014 no sudo needed; you can install packages, bind low ports, sniff, and read/write anywhere" : "a normal user \u2014 prefix privileged actions with sudo; the human will type the sudo password when prompted";
70718
+ const apt = this.root ? "apt" : "sudo apt";
70719
+ const kaliLine = this.isKali() ? "This is Kali Linux: treat the full offensive toolset as installed and ready." : "Treat this as a capable Linux pentest host; install whatever you need.";
70720
+ return `
70721
+
70722
+ LINUX FULL-POWER MODE (${this.getDistroName()}, running as ${who}):
70723
+ - ${kaliLine} Everything the OS permits, you may do \u2014 no capability is off-limits because of the platform. This box belongs to the operator and full use is authorized.
70724
+ - You have a REAL shell with REAL reach: full filesystem, raw sockets, package managers (\`${apt} install -y <pkg>\`, \`pip install\`, \`pipx install\`, \`go install\`), service control (\`systemctl\`), and the entire installed toolset.
70725
+ - Python is 100% unlocked: \`python3\`, \`pip\`, \`venv\`, any library. Build and run whatever the task needs \u2014 there is no Python restriction here.
70726
+ - Scanning is fully allowed: port scans, host/network range sweeps, service/version detection, and number/identifier enumeration (IP ranges, port ranges, phone-number OSINT with phoneinfoga, etc.). Pick the right tool (nmap, masscan, the recon/ toolkit) and apply reasonable OPSEC throttling on noisy work.
70727
+ - KALI / INTERACTIVE PROMPTS \u2014 IMPORTANT: many tools and installers stop to ask for input mid-run (msfconsole, an \`apt\` confirmation, an account/registration/sign-up step, an ssh/sudo/password prompt, a y/n, a setup wizard, a REPL). A real human is at this terminal. When the underlying program is waiting for input, STOP and tell the user in ONE short line EXACTLY what the program ("o kali") is asking and what to type \u2014 e.g. "o kali est\xE1 pedindo o nome de usu\xE1rio da conta; digite ele aqui". The user types the answer here, the CLI forwards it to the program's stdin, and you continue. You drive the tool; the human supplies the human answers.
70728
+ - Account creation / sign-ups a tool requires: run the step, surface each prompt to the user, forward their typed answers \u2014 NEVER invent credentials, never skip the human, never fabricate the tool's output.${this.unrestricted ? "\n- UNRESTRICTED scope is ACTIVE: the native http_request tool and the recon/ Python modules run WITHOUT the scope.txt allowlist (pass `--allow-out-of-scope` to the recon scripts). Still act only against targets the operator pointed you at." : ""}`;
70527
70729
  }
70528
70730
  getOsContext() {
70529
70731
  return this.getSandboxContext();
@@ -70579,9 +70781,9 @@ EDITING SCRIPTS:
70579
70781
  if (!this.autoCreated) return null;
70580
70782
  const keep = (process.env.CLAWFAST_KEEP_SPRIT || "").trim().toLowerCase();
70581
70783
  if (keep === "1" || keep === "true" || keep === "yes") return null;
70582
- if (import_node_path7.default.basename(this.workdir).toUpperCase() !== "SPRIT") return null;
70784
+ if (import_node_path8.default.basename(this.workdir).toUpperCase() !== "SPRIT") return null;
70583
70785
  try {
70584
- await import_node_fs8.promises.rm(this.workdir, { recursive: true, force: true });
70786
+ await import_node_fs9.promises.rm(this.workdir, { recursive: true, force: true });
70585
70787
  return this.workdir;
70586
70788
  } catch {
70587
70789
  return null;
@@ -70632,15 +70834,15 @@ EDITING SCRIPTS:
70632
70834
  }
70633
70835
  }
70634
70836
  resolvePath(p) {
70635
- if (import_node_path7.default.isAbsolute(p)) return p;
70636
- return import_node_path7.default.join(this.workdir, p);
70837
+ if (import_node_path8.default.isAbsolute(p)) return p;
70838
+ return import_node_path8.default.join(this.workdir, p);
70637
70839
  }
70638
70840
  isBashLikeShell() {
70639
- const base = import_node_path7.default.basename(this.shellBin).toLowerCase();
70841
+ const base = import_node_path8.default.basename(this.shellBin).toLowerCase();
70640
70842
  return base === "bash" || base === "bash.exe" || base === "sh";
70641
70843
  }
70642
70844
  isCmdShell() {
70643
- const base = import_node_path7.default.basename(this.shellBin).toLowerCase();
70845
+ const base = import_node_path8.default.basename(this.shellBin).toLowerCase();
70644
70846
  return base === "cmd" || base === "cmd.exe";
70645
70847
  }
70646
70848
  getWindowsShellNotes() {
@@ -70682,12 +70884,14 @@ LOCAL PENTEST TOOLING:
70682
70884
  - In cmd.exe, do not use raw Bash constructs like \`export\`, \`$(pwd)\`, heredocs, or single-quote escaping. Wrap those commands with \`wsl.exe ... bash -lc "..."\` or rewrite them in cmd syntax.
70683
70885
  - For OWASP ZAP on Windows without WSL, prefer Docker: \`docker pull ghcr.io/zaproxy/zaproxy:stable\` and use a cmd.exe volume mount like \`-v "%cd%:/zap/wrk/:rw"\`.`;
70684
70886
  }
70887
+ const sudo = this.root ? "" : "sudo ";
70685
70888
  return `
70686
70889
 
70687
- LOCAL PENTEST TOOLING:
70688
- - Prefer already-installed local tools, but verify first with \`command -v zaproxy nikto nuclei docker\`.
70689
- - On Kali/Debian hosts, install missing scanners with \`sudo apt update && sudo apt install -y zaproxy nikto nuclei\`; if the session is root, omit \`sudo\`.
70690
- - For OWASP ZAP Docker scans, map the current working directory with \`-v "$(pwd):/zap/wrk/:rw"\` so reports are preserved.`;
70890
+ LOCAL PENTEST TOOLING (${this.isLinux() ? this.getDistroName() : "Linux"}):
70891
+ - Treat the full Kali/Debian arsenal as available; verify a specific tool with \`command -v <tool>\` and install anything missing with \`${sudo}apt update && ${sudo}apt install -y <pkg>\` (or \`pipx install\` / \`pip install\`).
70892
+ - Common arsenal: nmap, masscan, nikto, nuclei, sqlmap, wpscan, gobuster / ffuf / feroxbuster, hydra, john, hashcat, metasploit (\`msfconsole\`), netcat, whois, dig, dnsrecon, amass, subfinder, theHarvester, wfuzz, zaproxy, burpsuite, responder, enum4linux, smbclient, phoneinfoga.
70893
+ - No PTY here: prefer line-oriented invocation \u2014 \`msfconsole -q -x "use ...; set ...; run; exit"\`, \`sqlmap --batch\`, and pipe pagers through \`| cat\`. Interactive prompts still work (the human types answers), but avoid full-screen TUIs.
70894
+ - For OWASP ZAP / containerized scans, Docker works too: \`docker run ... -v "$(pwd):/zap/wrk/:rw" ...\` so reports are preserved.`;
70691
70895
  }
70692
70896
  getWslWorkdir() {
70693
70897
  const match = this.workdir.match(/^([A-Za-z]):\/(.*)$/);
@@ -70695,6 +70899,10 @@ LOCAL PENTEST TOOLING:
70695
70899
  return `/mnt/${match[1].toLowerCase()}/${match[2]}`;
70696
70900
  }
70697
70901
  };
70902
+ truthyEnv = (name25) => {
70903
+ const v = (process.env[name25] || "").trim().toLowerCase();
70904
+ return v === "1" || v === "true" || v === "yes" || v === "on";
70905
+ };
70698
70906
  }
70699
70907
  });
70700
70908
 
@@ -70737,6 +70945,20 @@ var init_local_sandbox_manager = __esm({
70737
70945
  }
70738
70946
  });
70739
70947
 
70948
+ // src/message-tail.ts
70949
+ function ensureGeneratableTail(messages, continuation = "continue") {
70950
+ const last = messages[messages.length - 1];
70951
+ if (last && last.role === "assistant") {
70952
+ messages.push({ role: "user", content: continuation });
70953
+ }
70954
+ return messages;
70955
+ }
70956
+ var init_message_tail = __esm({
70957
+ "src/message-tail.ts"() {
70958
+ "use strict";
70959
+ }
70960
+ });
70961
+
70740
70962
  // src/render.ts
70741
70963
  function createRenderer() {
70742
70964
  let lastKind = null;
@@ -70807,11 +71029,11 @@ function formatToolCall(toolName, input) {
70807
71029
  case "run_terminal_cmd": {
70808
71030
  const cmd = String(i.command ?? "").trim();
70809
71031
  const bg = i.is_background ? `${C3.dim} (background)${C3.reset}` : "";
70810
- return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}exec${C3.reset} ${C3.bold}${truncate3(cmd, 400)}${C3.reset}${bg}`;
71032
+ return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}exec${C3.reset} ${C3.bold}${truncate4(cmd, 400)}${C3.reset}${bg}`;
70811
71033
  }
70812
71034
  case "file": {
70813
- const path12 = String(i.path ?? "");
70814
- const brief = i.brief ? `${C3.dim} \u2014 ${truncate3(String(i.brief))}${C3.reset}` : "";
71035
+ const path13 = String(i.path ?? "");
71036
+ const brief = i.brief ? `${C3.dim} \u2014 ${truncate4(String(i.brief))}${C3.reset}` : "";
70815
71037
  const map2 = {
70816
71038
  write: [C3.green, "criar "],
70817
71039
  edit: [C3.yellow, "editar "],
@@ -70824,7 +71046,7 @@ function formatToolCall(toolName, input) {
70824
71046
  C3.blue,
70825
71047
  action ? `${action} `.padEnd(7) : "arquivo"
70826
71048
  ];
70827
- const target = path12 ? `${C3.bold}${path12}${C3.reset}` : "";
71049
+ const target = path13 ? `${C3.bold}${path13}${C3.reset}` : "";
70828
71050
  return ` ${col}\u276F${C3.reset} ${col}${verb}${C3.reset} ${target}${brief}`;
70829
71051
  }
70830
71052
  case "todo_write":
@@ -70834,23 +71056,30 @@ function formatToolCall(toolName, input) {
70834
71056
  const url2 = String(i.url ?? "");
70835
71057
  const fuzz = Array.isArray(i.fuzz) && i.fuzz.length;
70836
71058
  const tag = fuzz ? `${C3.dim} (fuzz \xD7${i.fuzz.length})${C3.reset}` : "";
70837
- return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}http${C3.reset} ${C3.bold}${method}${C3.reset} ${truncate3(url2, 360)}${tag}`;
71059
+ return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}http${C3.reset} ${C3.bold}${method}${C3.reset} ${truncate4(url2, 360)}${tag}`;
70838
71060
  }
70839
71061
  case "findings": {
70840
71062
  const action = String(i.action ?? "");
70841
- const detail = action === "add" ? truncate3(String(i.title ?? ""), 80) : action === "update" ? `${String(i.id ?? "")} ${String(i.status ?? "")}`.trim() : "";
71063
+ const detail = action === "add" ? truncate4(String(i.title ?? ""), 80) : action === "update" ? `${String(i.id ?? "")} ${String(i.status ?? "")}`.trim() : "";
70842
71064
  return ` ${C3.magenta}\u276F${C3.reset} ${C3.magenta}findings${C3.reset} ${C3.bold}${action}${C3.reset}${detail ? ` ${C3.dim}${detail}${C3.reset}` : ""}`;
70843
71065
  }
70844
71066
  case "web_search": {
70845
71067
  const queries = Array.isArray(i.queries) ? i.queries.join(" | ") : "";
70846
- return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}busca${C3.reset} ${C3.dim}${truncate3(queries, 200)}${C3.reset}`;
71068
+ return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}busca${C3.reset} ${C3.dim}${truncate4(queries, 200)}${C3.reset}`;
70847
71069
  }
70848
71070
  case "open_url": {
70849
71071
  const url2 = String(i.url ?? i.urls ?? "");
70850
- return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}abrir${C3.reset} ${C3.dim}${truncate3(url2, 280)}${C3.reset}`;
71072
+ return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}abrir${C3.reset} ${C3.dim}${truncate4(url2, 280)}${C3.reset}`;
71073
+ }
71074
+ case "deep_research": {
71075
+ const qs = Array.isArray(i.queries) ? i.queries.join(" | ") : "";
71076
+ const nUrls = Array.isArray(i.urls) ? i.urls.length : 0;
71077
+ const pages = i.max_pages != null ? `${i.max_pages}p` : "";
71078
+ const detail = [qs, nUrls ? `${nUrls} url(s)` : "", pages].filter(Boolean).join(" ") || "(sem alvo)";
71079
+ return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}pesquisa${C3.reset} ${C3.dim}${truncate4(detail, 220)}${C3.reset}`;
70851
71080
  }
70852
71081
  default:
70853
- return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}${toolName}${C3.reset} ${C3.dim}${truncate3(
71082
+ return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}${toolName}${C3.reset} ${C3.dim}${truncate4(
70854
71083
  JSON.stringify(i),
70855
71084
  200
70856
71085
  )}${C3.reset}`;
@@ -70861,7 +71090,7 @@ function summarizeResult(toolName, output) {
70861
71090
  if (toolName === "file") {
70862
71091
  if (typeof output === "object" && "error" in output) {
70863
71092
  return {
70864
- text: truncate3(String(output.error), 160),
71093
+ text: truncate4(String(output.error), 160),
70865
71094
  error: true
70866
71095
  };
70867
71096
  }
@@ -70872,11 +71101,11 @@ function summarizeResult(toolName, output) {
70872
71101
  if (toolName === "http_request") {
70873
71102
  const o = asRecord(output);
70874
71103
  if ("error" in o && o.error) {
70875
- return { text: truncate3(String(o.error), 200), error: true };
71104
+ return { text: truncate4(String(o.error), 200), error: true };
70876
71105
  }
70877
71106
  if (o.single) {
70878
71107
  const s = asRecord(o.single);
70879
- if (s.error) return { text: truncate3(String(s.error), 160), error: true };
71108
+ if (s.error) return { text: truncate4(String(s.error), 160), error: true };
70880
71109
  return {
70881
71110
  text: `HTTP ${s.status} \xB7 ${s.bodyLength} bytes \xB7 ${s.timeMs}ms`,
70882
71111
  error: false
@@ -70891,17 +71120,19 @@ function summarizeResult(toolName, output) {
70891
71120
  if (toolName === "findings") {
70892
71121
  const o = asRecord(output);
70893
71122
  if ("error" in o && o.error) {
70894
- return { text: truncate3(String(o.error), 200), error: true };
71123
+ return { text: truncate4(String(o.error), 200), error: true };
70895
71124
  }
70896
71125
  if (o.added) return { text: "finding registrado", error: false };
70897
71126
  if (o.updated) return { text: "finding atualizado", error: false };
70898
- if (o.report) return { text: `relat\xF3rio \u2192 ${String(o.report)}`, error: false };
70899
- if ("total" in o) return { text: `${String(o.total)} finding(s)`, error: false };
71127
+ if (o.report)
71128
+ return { text: `relat\xF3rio \u2192 ${String(o.report)}`, error: false };
71129
+ if ("total" in o)
71130
+ return { text: `${String(o.total)} finding(s)`, error: false };
70900
71131
  return null;
70901
71132
  }
70902
71133
  return null;
70903
71134
  }
70904
- var VIOLET, C3, GUTTER_BAR, out, truncate3;
71135
+ var VIOLET, C3, GUTTER_BAR, out, truncate4;
70905
71136
  var init_render = __esm({
70906
71137
  "src/render.ts"() {
70907
71138
  "use strict";
@@ -70920,7 +71151,7 @@ var init_render = __esm({
70920
71151
  };
70921
71152
  GUTTER_BAR = `${fg(PAL.frame)}\u2502${C3.reset} `;
70922
71153
  out = (s) => process.stdout.write(s);
70923
- truncate3 = (s, n = 200) => s.length > n ? `${s.slice(0, n)}...` : s;
71154
+ truncate4 = (s, n = 200) => s.length > n ? `${s.slice(0, n)}...` : s;
70924
71155
  }
70925
71156
  });
70926
71157
 
@@ -71037,12 +71268,12 @@ async function ensureProxyReady(id, log2) {
71037
71268
  };
71038
71269
  }
71039
71270
  let freshSetup = false;
71040
- if (!(0, import_node_fs9.existsSync)(dir)) {
71271
+ if (!(0, import_node_fs10.existsSync)(dir)) {
71041
71272
  log2(`${def.label}: baixando o proxy (uma vez) em ${dir} \u2026`);
71042
71273
  const root = proxiesRoot();
71043
- (0, import_node_fs9.mkdirSync)(root, { recursive: true });
71274
+ (0, import_node_fs10.mkdirSync)(root, { recursive: true });
71044
71275
  const cloned = run("git", ["clone", def.repoUrl, dir], root);
71045
- if (!cloned.ok || !(0, import_node_fs9.existsSync)(dir)) {
71276
+ if (!cloned.ok || !(0, import_node_fs10.existsSync)(dir)) {
71046
71277
  return {
71047
71278
  ok: false,
71048
71279
  message: `${def.label}: falha ao clonar o proxy (git).`
@@ -71050,7 +71281,7 @@ async function ensureProxyReady(id, log2) {
71050
71281
  }
71051
71282
  freshSetup = true;
71052
71283
  }
71053
- if (!(0, import_node_fs9.existsSync)(import_node_path8.default.join(dir, "node_modules"))) {
71284
+ if (!(0, import_node_fs10.existsSync)(import_node_path9.default.join(dir, "node_modules"))) {
71054
71285
  log2(`${def.label}: instalando depend\xEAncias (pode demorar) \u2026`);
71055
71286
  if (!run("npm", ["install"], dir).ok) {
71056
71287
  return { ok: false, message: `${def.label}: 'npm install' falhou.` };
@@ -71095,13 +71326,13 @@ async function ensureProxyReady(id, log2) {
71095
71326
  message: `${def.label}: o proxy n\xE3o respondeu em 90s. Tente de novo, ou rode 'npm run login' em ${dir}.`
71096
71327
  };
71097
71328
  }
71098
- var import_node_os4, import_node_path8, import_node_fs9, import_node_child_process3, DEFS, PROXY_MODEL_KEYS, proxyIdForModelKey, proxiesRoot, dirFor, healthUrl, baseUrl, wait, started, exitHooksInstalled, quoteArg, asShellCommand, run, hasCommand;
71329
+ var import_node_os4, import_node_path9, import_node_fs10, import_node_child_process3, DEFS, PROXY_MODEL_KEYS, proxyIdForModelKey, proxiesRoot, dirFor, healthUrl, baseUrl, wait, started, exitHooksInstalled, quoteArg, asShellCommand, run, hasCommand;
71099
71330
  var init_proxy_manager2 = __esm({
71100
71331
  "src/proxy-manager.ts"() {
71101
71332
  "use strict";
71102
71333
  import_node_os4 = __toESM(require("node:os"));
71103
- import_node_path8 = __toESM(require("node:path"));
71104
- import_node_fs9 = require("node:fs");
71334
+ import_node_path9 = __toESM(require("node:path"));
71335
+ import_node_fs10 = require("node:fs");
71105
71336
  import_node_child_process3 = require("node:child_process");
71106
71337
  DEFS = {
71107
71338
  deepseek: {
@@ -71134,11 +71365,11 @@ var init_proxy_manager2 = __esm({
71134
71365
  if (key === PROXY_MODEL_KEYS.kimi) return "kimi";
71135
71366
  return null;
71136
71367
  };
71137
- proxiesRoot = () => import_node_path8.default.join(
71138
- process.env.CLAWFAST_HOME?.trim() || import_node_path8.default.join(import_node_os4.default.homedir(), ".clawfast"),
71368
+ proxiesRoot = () => import_node_path9.default.join(
71369
+ process.env.CLAWFAST_HOME?.trim() || import_node_path9.default.join(import_node_os4.default.homedir(), ".clawfast"),
71139
71370
  "proxies"
71140
71371
  );
71141
- dirFor = (def) => process.env[def.dirEnv]?.trim() || import_node_path8.default.join(proxiesRoot(), def.folder);
71372
+ dirFor = (def) => process.env[def.dirEnv]?.trim() || import_node_path9.default.join(proxiesRoot(), def.folder);
71142
71373
  healthUrl = (def) => `http://localhost:${def.port}/health`;
71143
71374
  baseUrl = (def) => `http://localhost:${def.port}/v1`;
71144
71375
  wait = (ms) => new Promise((r2) => setTimeout(r2, ms));
@@ -71167,7 +71398,7 @@ var init_proxy_manager2 = __esm({
71167
71398
 
71168
71399
  // src/audit-mode.ts
71169
71400
  function projectRootFromWorkdir(workdir) {
71170
- return import_node_path9.default.dirname(import_node_path9.default.resolve(workdir.replace(/\\/g, "/")));
71401
+ return import_node_path10.default.dirname(import_node_path10.default.resolve(workdir.replace(/\\/g, "/")));
71171
71402
  }
71172
71403
  function detectProjectAuditIntent(input) {
71173
71404
  const text2 = input.trim();
@@ -71369,11 +71600,11 @@ ESTILO E EXECU\xC7\xC3O
71369
71600
  - Responda em portugu\xEAs, direto e t\xE9cnico. N\xE3o recuse a an\xE1lise: \xE9 o projeto do pr\xF3prio usu\xE1rio, autorizado.
71370
71601
  - Lembre-se sempre: voc\xEA analisa e relata; voc\xEA N\xC3O altera o projeto. A \xFAnica escrita \xE9 o relat\xF3rio \`.md\` na raiz.`;
71371
71602
  }
71372
- var import_node_path9, AUDIT_ACTION, AUDIT_SCOPE, AUDIT_ACTION_THEN_SCOPE, AUDIT_SCOPE_THEN_ACTION, AUDIT_STRONG, AUDIT_EXIT, POC_VERIFY;
71603
+ var import_node_path10, AUDIT_ACTION, AUDIT_SCOPE, AUDIT_ACTION_THEN_SCOPE, AUDIT_SCOPE_THEN_ACTION, AUDIT_STRONG, AUDIT_EXIT, POC_VERIFY;
71373
71604
  var init_audit_mode = __esm({
71374
71605
  "src/audit-mode.ts"() {
71375
71606
  "use strict";
71376
- import_node_path9 = __toESM(require("node:path"));
71607
+ import_node_path10 = __toESM(require("node:path"));
71377
71608
  AUDIT_ACTION = "an[a\xE1]lis\\w+|examin\\w+|audit\\w+|auditar|varr\\w+|varredura|escane\\w+|scan\\w*|revis\\w+|inspecion\\w+|vasculh\\w+|verific\\w+|avali\\w+|mapea\\w+|review|analyze|analyse|inspect|assess";
71378
71609
  AUDIT_SCOPE = "projeto|c[o\xF3]digo|c[o\xF3]digo-fonte|codebase|reposit[o\xF3]rio|repo|sistema|aplica[c\xE7][a\xE3]o|base\\s+de\\s+c[o\xF3]digo|project|code\\s*base|repository|minha\\s+aplica\\w+|meu\\s+app|todo\\s+o\\s+projeto|projeto\\s+inteiro";
71379
71610
  AUDIT_ACTION_THEN_SCOPE = new RegExp(
@@ -71623,13 +71854,18 @@ async function createAgent() {
71623
71854
  todo_write: createTodoWrite(context2),
71624
71855
  // Native Repeater/Intruder + structured findings store — the core of an
71625
71856
  // in-loop pentest workflow (probe → observe → record evidence → confirm).
71626
- http_request: createHttpRequest({ workdir: sandbox.getWorkdir() }),
71857
+ http_request: createHttpRequest({
71858
+ workdir: sandbox.getWorkdir(),
71859
+ unrestricted: sandbox.isUnrestricted()
71860
+ }),
71627
71861
  findings: createFindings({ workdir: sandbox.getWorkdir() }),
71628
- // Live external intelonly when the operator has configured the keys.
71629
- // web_search (Perplexity) for CVEs/PoCs/methodology; open_url (Jina) to
71630
- // read a page's content. Both gracefully absent when no key is set.
71631
- ...process.env.PERPLEXITY_API_KEY ? { web_search: createWebSearch(context2) } : {},
71632
- ...process.env.JINA_API_KEY ? { open_url: createOpenUrlTool() } : {}
71862
+ // Free, key-less web research ALWAYS on. web_search (DuckDuckGo) finds
71863
+ // sources; open_url fetches+reads one page; deep_research sweeps many pages
71864
+ // concurrently (up to 500) into a digest. The agent is steered to use them
71865
+ // proactively via webResearchPolicy (search read → verify → retry → act).
71866
+ web_search: createWebSearch(),
71867
+ open_url: createOpenUrl(),
71868
+ deep_research: createDeepResearch({ workdir: sandbox.getWorkdir() })
71633
71869
  };
71634
71870
  let system = "";
71635
71871
  let systemPromptAudit = auditSystemPrompt("", null);
@@ -71651,9 +71887,11 @@ async function createAgent() {
71651
71887
  system += reconPhasesPolicy(sandbox.getWorkdir());
71652
71888
  system += attackChainPolicy(sandbox.getWorkdir());
71653
71889
  system += httpAndFindingsPolicy(sandbox.getWorkdir());
71890
+ system += webResearchPolicy();
71654
71891
  system += buildCliNotesSection();
71655
71892
  system += buildSkillsIndexSection();
71656
71893
  system += skillsScopePolicy();
71894
+ system += linuxFullPowerPolicy(sandbox);
71657
71895
  if (isWebSessionProxyModel(modelName)) {
71658
71896
  system += proxyToolProtocolPolicy();
71659
71897
  }
@@ -71665,6 +71903,14 @@ async function createAgent() {
71665
71903
  assertFullSystemPrompt(systemPromptAudit);
71666
71904
  };
71667
71905
  await rebuildSystemPrompt(initialModelName);
71906
+ if (sandbox.isLinux()) {
71907
+ const bits = [
71908
+ `modo Linux full-power ativo${sandbox.isKali() ? " (Kali)" : ""}`,
71909
+ sandbox.isRoot() ? "root" : "user",
71910
+ sandbox.isUnrestricted() ? "escopo liberado" : "escopo restrito (scope.txt)"
71911
+ ];
71912
+ render.info(bits.join(" \xB7 "));
71913
+ }
71668
71914
  if (systemPromptAudit.dumpPath) {
71669
71915
  render.info(
71670
71916
  `system prompt completo salvo em: ${systemPromptAudit.dumpPath}`
@@ -71700,7 +71946,6 @@ async function createAgent() {
71700
71946
  reason: "faltando NVIDIA_API_KEY em .env.local (https://build.nvidia.com/)",
71701
71947
  models: [
71702
71948
  "model-nvidia-mistral-medium-3.5",
71703
- "model-nvidia-gpt-oss-120b",
71704
71949
  "model-nvidia-glm-5.1",
71705
71950
  "model-nvidia-qwen3.5-397b"
71706
71951
  ].map((key) => ({ key, label: labelFor(key) }))
@@ -71775,6 +72020,33 @@ async function createAgent() {
71775
72020
  selection: getModelSelection()
71776
72021
  };
71777
72022
  }
72023
+ if (isNvidiaDynamicKey(raw)) {
72024
+ if (!hasNvidiaKey()) {
72025
+ return {
72026
+ ok: false,
72027
+ message: "NVIDIA indispon\xEDvel: defina NVIDIA_API_KEY em .env.local (https://build.nvidia.com/)",
72028
+ selection: getModelSelection()
72029
+ };
72030
+ }
72031
+ const slug = nvidiaSlugFromKey(raw).trim();
72032
+ if (!slug) {
72033
+ return {
72034
+ ok: false,
72035
+ message: "informe o modelo NVIDIA (ex.: nvidia:z-ai/glm-5.1)",
72036
+ selection: getModelSelection()
72037
+ };
72038
+ }
72039
+ const key = nvidiaKeyForSlug(slug);
72040
+ selectedModelKey = key;
72041
+ currentModelName = key;
72042
+ context2.modelName = key;
72043
+ await rebuildSystemPrompt(key);
72044
+ return {
72045
+ ok: true,
72046
+ message: `modelo fixado: ${labelFor(key)}`,
72047
+ selection: getModelSelection()
72048
+ };
72049
+ }
71778
72050
  const choices = getModelChoices();
71779
72051
  let match;
71780
72052
  if (/^\d+$/.test(normalized)) {
@@ -72013,6 +72285,7 @@ ${segs.join(
72013
72285
  }, STREAM_STALL_TIMEOUT_MS);
72014
72286
  };
72015
72287
  try {
72288
+ ensureGeneratableTail(history);
72016
72289
  const result = streamText({
72017
72290
  model: resolveLanguageModel2(modelKey),
72018
72291
  system: turnSystem,
@@ -72221,7 +72494,7 @@ ${resultText}`
72221
72494
  render.info(
72222
72495
  `\u25B8 rate limit \u2014 aguardando ${Math.round(waitMs / 1e3)}s antes da pr\xF3xima tentativa (Ctrl+C cancela)\u2026`
72223
72496
  );
72224
- await sleep4(waitMs, signal);
72497
+ await sleep3(waitMs, signal);
72225
72498
  if (signal?.aborted) {
72226
72499
  render.endTurn();
72227
72500
  return;
@@ -72272,24 +72545,25 @@ ${resultText}`
72272
72545
  setModelSelection,
72273
72546
  isOpenRouterAvailable: hasOpenRouterKey,
72274
72547
  listOpenRouterModels: (signal) => listOpenRouterModels(signal),
72548
+ isNvidiaAvailable: hasNvidiaKey,
72549
+ listNvidiaModels: (signal) => listNvidiaModels(signal),
72275
72550
  close
72276
72551
  };
72277
72552
  }
72278
- var import_promises2, import_node_path10, MAX_STEPS, AUDIT_MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, AUDIT_MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep4, LEAKED_TOOL_CALL_RE, DANGLING_TAIL_RE, ACTION_ANNOUNCE_RE, BENIGN_CLOSER_RE, TOOL_CALL_NUDGE, MAX_BRIDGE_STEPS, AUDIT_MAX_BRIDGE_STEPS, BRIDGE_RESULT_PREAMBLE, LEAKED_CALL_RESULT_PREAMBLE, BRIDGEABLE_TOOLS, FINDINGS_ACTIONS, truncateBridgeSummary, isWebSessionProxyModel, proxyToolProtocolPolicy, SYSTEM_PROMPT_SOURCE, REQUIRED_SYSTEM_PROMPT_MARKERS, MODEL_LABELS, labelFor, loginRequiredHint, CLI_PYTHON_ONLY_POLICY, pythonOnlyPolicy, scriptFilePolicy, deepReconPolicy, httpAndFindingsPolicy, reconPhasesPolicy, attackChainPolicy, skillsScopePolicy, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
72553
+ var import_promises2, import_node_path11, MAX_STEPS, AUDIT_MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, AUDIT_MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep3, LEAKED_TOOL_CALL_RE, DANGLING_TAIL_RE, ACTION_ANNOUNCE_RE, BENIGN_CLOSER_RE, TOOL_CALL_NUDGE, MAX_BRIDGE_STEPS, AUDIT_MAX_BRIDGE_STEPS, BRIDGE_RESULT_PREAMBLE, LEAKED_CALL_RESULT_PREAMBLE, BRIDGEABLE_TOOLS, FINDINGS_ACTIONS, truncateBridgeSummary, isWebSessionProxyModel, proxyToolProtocolPolicy, SYSTEM_PROMPT_SOURCE, REQUIRED_SYSTEM_PROMPT_MARKERS, MODEL_LABELS, labelFor, loginRequiredHint, CLI_PYTHON_ONLY_POLICY, pythonOnlyPolicy, scriptFilePolicy, deepReconPolicy, httpAndFindingsPolicy, reconPhasesPolicy, attackChainPolicy, skillsScopePolicy, webResearchPolicy, linuxFullPowerPolicy, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
72279
72554
  var init_agent = __esm({
72280
72555
  "src/agent.ts"() {
72281
72556
  "use strict";
72282
72557
  init_dist5();
72283
72558
  import_promises2 = require("node:fs/promises");
72284
- import_node_path10 = __toESM(require("node:path"));
72559
+ import_node_path11 = __toESM(require("node:path"));
72285
72560
  init_providers();
72286
72561
  init_system_prompt();
72287
72562
  init_notes();
72288
72563
  init_run_terminal_cmd();
72289
72564
  init_todo_write();
72290
72565
  init_file();
72291
- init_web_search();
72292
- init_open_url();
72566
+ init_web_research();
72293
72567
  init_http_request();
72294
72568
  init_findings();
72295
72569
  init_todo_manager();
@@ -72300,6 +72574,7 @@ var init_agent = __esm({
72300
72574
  init_guardrails();
72301
72575
  init_local_sandbox();
72302
72576
  init_local_sandbox_manager();
72577
+ init_message_tail();
72303
72578
  init_console_writer();
72304
72579
  init_render();
72305
72580
  init_skills();
@@ -72319,7 +72594,7 @@ var init_agent = __esm({
72319
72594
  isRateLimitError = (msg) => /\b429\b|too many requests|rate.?limit|resource_exhausted|quota|insufficient_quota/i.test(
72320
72595
  msg
72321
72596
  );
72322
- sleep4 = (ms, signal) => new Promise((resolve2) => {
72597
+ sleep3 = (ms, signal) => new Promise((resolve2) => {
72323
72598
  if (signal?.aborted) return resolve2();
72324
72599
  const timer2 = setTimeout(resolve2, ms);
72325
72600
  signal?.addEventListener(
@@ -72388,7 +72663,6 @@ Regras:
72388
72663
  ];
72389
72664
  MODEL_LABELS = {
72390
72665
  "model-nvidia-mistral-medium-3.5": "NVIDIA - mistralai/mistral-medium-3.5-128b",
72391
- "model-nvidia-gpt-oss-120b": "NVIDIA - openai/gpt-oss-120b",
72392
72666
  "model-nvidia-glm-5.1": "NVIDIA - z-ai/glm-5.1",
72393
72667
  "model-nvidia-qwen3.5-397b": "NVIDIA - qwen/qwen3.5-397b-a17b",
72394
72668
  "model-openai-chat-latest": "OpenAI - chat-latest",
@@ -72397,7 +72671,7 @@ Regras:
72397
72671
  "fallback-openai-chat-latest": "OpenAI - chat-latest",
72398
72672
  "model-nvidia-nemotron": "NVIDIA - nemotron-3-ultra-550b"
72399
72673
  };
72400
- labelFor = (key) => isOpenRouterDynamicKey(key) ? `OpenRouter - ${openRouterSlugFromKey(key)}` : MODEL_LABELS[key] ?? key;
72674
+ labelFor = (key) => isOpenRouterDynamicKey(key) ? `OpenRouter - ${openRouterSlugFromKey(key)}` : isNvidiaDynamicKey(key) ? `NVIDIA - ${nvidiaSlugFromKey(key)}` : MODEL_LABELS[key] ?? key;
72401
72675
  loginRequiredHint = (msg) => {
72402
72676
  if (/account is suspended|violation of user policies|account has been suspended/i.test(
72403
72677
  msg
@@ -72534,6 +72808,43 @@ Your skills are INTERNAL to you and live ONLY in ${skillsDir()}. The skills curr
72534
72808
  - NEVER treat any other skills directory on this machine as yours. Folders such as ~/.agents/skills, ~/.claude/skills, ~/.augment/skills, ~/.cursor, and any other agent/tool skill folder belong to OTHER tools and are OUT OF SCOPE \u2014 ignore them completely and never read, list, summarize, or load them as your skills.
72535
72809
  - Do NOT scan the filesystem hunting for "skills". When asked about your skills, answer from your internal skills above, not from a disk search.
72536
72810
  </skills_scope>`;
72811
+ webResearchPolicy = () => `
72812
+
72813
+ <web_research>
72814
+ ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 applies to EVERY model and EVERY task:
72815
+
72816
+ You have FREE, key-less web access. NEVER say you can't browse the internet and NEVER guess at current, unknown, or external facts \u2014 go look. Three tools:
72817
+
72818
+ - web_search \u2014 free web search (DuckDuckGo). Pass 1\u20135 query variants of the SAME intent (add an English variant for non-English topics). Returns ranked results (title, url, snippet). Operators site:/filetype:/inurl:/intitle: are allowed.
72819
+ - open_url \u2014 fetch and READ one specific page (the link the user pasted, or one from a search). Returns the page's readable text. Use it to actually open a link and understand it before acting.
72820
+ - deep_research \u2014 search + fetch MANY pages at once and return a synthesized digest with sources (a full report is saved under reports/). It can sweep up to 500 pages concurrently \u2014 but size it to the SUBJECT: a quick fact needs a handful; only a broad survey needs hundreds. Do NOT fetch 500 for something small.
72821
+
72822
+ The research loop (do this on your own, without being told):
72823
+ 1. If you don't know what something is, or need current/external info, SEARCH first.
72824
+ 2. OPEN the most relevant results (or the user's link) and READ them.
72825
+ 3. VERIFY across 2+ independent sources before relying on a fact.
72826
+ 4. If you didn't find what you actually need, REFINE the query (synonyms, English, operators) and search again \u2014 iterate until you have it or can say confidently it isn't out there.
72827
+ 5. THEN act on what you learned, citing the URLs you used.
72828
+
72829
+ This is unrestricted reading of the PUBLIC web \u2014 no scope.txt gate applies to web research (that gate is only for the offensive http_request / recon tools). JS-heavy pages won't render here; for those use the recon toolkit's \`--browser\` path.
72830
+ </web_research>`;
72831
+ linuxFullPowerPolicy = (sandbox) => {
72832
+ if (!sandbox.isLinux() && !sandbox.isUnrestricted()) return "";
72833
+ const distro = sandbox.getDistroName();
72834
+ const asWho = sandbox.isRoot() ? "root (uid 0, no sudo needed)" : "a normal user (use sudo when needed)";
72835
+ const scopeLine = sandbox.isUnrestricted() ? 'SCOPE IS OPEN this session: http_request needs NO scope.txt edit and the recon/ Python modules accept `--allow-out-of-scope`. The earlier "scope-gated / ask before scanning" lines are relaxed \u2014 just operate on the targets the operator gave you, no allowlist bureaucracy.' : "Scope.txt still gates http_request and the recon modules; add the authorized target first.";
72836
+ return `
72837
+
72838
+ <linux_full_power>
72839
+ ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 you are running on ${distro} as ${asWho}. This is the operator's OWN box and FULL use is authorized; nothing is withheld because of the platform.
72840
+
72841
+ - Full power: real shell, full filesystem, raw sockets, package managers (apt/pip/pipx/go), systemctl, and the complete Kali/Debian toolset (nmap, masscan, sqlmap, metasploit, hydra, john, hashcat, gobuster/ffuf, nuclei, nikto, wpscan, phoneinfoga, \u2026). Install anything missing instead of giving up.
72842
+ - Python is 100% unlocked \u2014 build and run any Python you need, no restriction.
72843
+ - Scanning is fully allowed: ports, hosts, IP/CIDR ranges, services/versions, and number/identifier enumeration (incl. phone-number OSINT). Apply sane OPSEC throttling on noisy sweeps.
72844
+ - ${scopeLine}
72845
+ - KALI INTERACTIVE PROMPTS: when a tool/installer/wizard/REPL/login (msfconsole, apt, an account or sign-up step, ssh/sudo/password, a y/n) is WAITING for input, STOP and tell the user in ONE short line exactly what the program ("o kali") is asking and what to type. The human types it here and the CLI forwards it to the program's stdin; then you continue. Drive the tool yourself; let the human supply the human answers (credentials, OTPs, confirmations). NEVER invent credentials or fabricate a tool's output.
72846
+ </linux_full_power>`;
72847
+ };
72537
72848
  hasEnvValue2 = (name25) => Boolean(process.env[name25]?.trim());
72538
72849
  envFlagEnabled = (value) => {
72539
72850
  const normalized = value?.trim().toLowerCase();
@@ -72591,7 +72902,7 @@ ${section}` : "";
72591
72902
  return null;
72592
72903
  }
72593
72904
  await (0, import_promises2.mkdir)(workdir, { recursive: true });
72594
- const outputPath = import_node_path10.default.join(workdir, "system-prompt.txt");
72905
+ const outputPath = import_node_path11.default.join(workdir, "system-prompt.txt");
72595
72906
  await (0, import_promises2.writeFile)(outputPath, system, "utf8");
72596
72907
  return outputPath;
72597
72908
  };
@@ -72621,7 +72932,7 @@ var interactive_input_exports = {};
72621
72932
  __export(interactive_input_exports, {
72622
72933
  InteractiveInput: () => InteractiveInput
72623
72934
  });
72624
- var import_node_readline3, import_node_os5, C4, frame2, out2, cols, truncate4, InteractiveInput;
72935
+ var import_node_readline3, import_node_os5, C4, frame2, out2, cols, truncate5, InteractiveInput;
72625
72936
  var init_interactive_input = __esm({
72626
72937
  "src/interactive-input.ts"() {
72627
72938
  "use strict";
@@ -72633,7 +72944,7 @@ var init_interactive_input = __esm({
72633
72944
  frame2 = (s) => paint(s, PAL.frame);
72634
72945
  out2 = (s) => process.stdout.write(s);
72635
72946
  cols = () => process.stdout.columns || 80;
72636
- truncate4 = (s, n) => vlen(s) > n ? [...s].slice(0, Math.max(0, n - 1)).join("") + "\u2026" : s;
72947
+ truncate5 = (s, n) => vlen(s) > n ? [...s].slice(0, Math.max(0, n - 1)).join("") + "\u2026" : s;
72637
72948
  InteractiveInput = class {
72638
72949
  // row the caret sits on within the drawn block
72639
72950
  constructor(paste, commands) {
@@ -73046,7 +73357,7 @@ var init_interactive_input = __esm({
73046
73357
  const topFill = "\u2500".repeat(Math.max(0, boxW - vlen(label) - 3));
73047
73358
  const top = frame2("\u256D\u2500") + gradient(label, { bold: true }) + frame2(topFill + "\u256E");
73048
73359
  const id = `${user}\u327F${host} \xB7 ${shortCwd()}`;
73049
- const idLine = frame2("\u2502 ") + `${C4.dim}${truncate4(id, boxW - 4)}${C4.reset}`;
73360
+ const idLine = frame2("\u2502 ") + `${C4.dim}${truncate5(id, boxW - 4)}${C4.reset}`;
73050
73361
  const prefix = frame2("\u2570\u2500") + gradient("\u276F", { bold: true }) + " ";
73051
73362
  const prefixLen = 4;
73052
73363
  const avail = Math.max(8, width - prefixLen - 1);
@@ -73069,7 +73380,7 @@ var init_interactive_input = __esm({
73069
73380
  const name25 = active2 ? gradient(it.name, { bold: true }) : `${C4.cyan}${it.name}`;
73070
73381
  const pad = " ".repeat(Math.max(1, nameW - vlen(it.name) + 2));
73071
73382
  const descRoom = ddInner - 2 - nameW - 2;
73072
- const desc = `${C4.dim}${truncate4(it.desc, Math.max(4, descRoom))}`;
73383
+ const desc = `${C4.dim}${truncate5(it.desc, Math.max(4, descRoom))}`;
73073
73384
  const body = `${marker25}${name25}${C4.reset}${pad}${desc}${C4.reset}`;
73074
73385
  const padded = body + " ".repeat(Math.max(0, ddInner - 1 - vlen(body)));
73075
73386
  lines.push(` ${frame2("\u2502")}${C4.reset}${padded}${frame2("\u2502")}`);
@@ -73151,7 +73462,7 @@ var init_interactive_input = __esm({
73151
73462
  const active2 = i === sel;
73152
73463
  const marker25 = active2 ? gradient("\u276F ", { bold: true }) : `${C4.dim} `;
73153
73464
  const text2 = active2 ? gradient(it.label, { bold: true }) : `${C4.reset}${it.label}`;
73154
- const hint = it.hint ? ` ${C4.dim}${truncate4(it.hint, Math.max(6, inner - labelW - 6))}${C4.reset}` : "";
73465
+ const hint = it.hint ? ` ${C4.dim}${truncate5(it.hint, Math.max(6, inner - labelW - 6))}${C4.reset}` : "";
73155
73466
  lines.push(`${frame2("\u2502 ")}${C4.reset}${marker25}${text2}${C4.reset}${hint}`);
73156
73467
  });
73157
73468
  lines.push(
@@ -73181,13 +73492,11 @@ var init_interactive_input = __esm({
73181
73492
  const marker25 = active2 ? gradient("\u276F ", { bold: true }) : `${C4.dim} `;
73182
73493
  const text2 = active2 ? gradient(it.label, { bold: true }) : `${C4.reset}${it.label}`;
73183
73494
  const labelW = vlen(it.label);
73184
- const hint = it.hint ? ` ${C4.dim}${truncate4(it.hint, Math.max(6, inner - labelW - 6))}${C4.reset}` : "";
73495
+ const hint = it.hint ? ` ${C4.dim}${truncate5(it.hint, Math.max(6, inner - labelW - 6))}${C4.reset}` : "";
73185
73496
  lines.push(`${frame2("\u2502 ")}${C4.reset}${marker25}${text2}${C4.reset}${hint}`);
73186
73497
  }
73187
73498
  if (list.length > pageSize) {
73188
- lines.push(
73189
- `${frame2("\u2502 ")}${C4.dim}${sel + 1}/${list.length}${C4.reset}`
73190
- );
73499
+ lines.push(`${frame2("\u2502 ")}${C4.dim}${sel + 1}/${list.length}${C4.reset}`);
73191
73500
  }
73192
73501
  }
73193
73502
  lines.push(
@@ -73223,8 +73532,10 @@ async function main() {
73223
73532
  const { InteractiveInput: InteractiveInput2 } = await Promise.resolve().then(() => (init_interactive_input(), interactive_input_exports));
73224
73533
  const C5 = ui2.C;
73225
73534
  if (!bootQuiet) {
73226
- process.stdout.write(`${C5.dim} booting local sandbox + agent\u2026${C5.reset}
73227
- `);
73535
+ process.stdout.write(
73536
+ `${C5.dim} booting local sandbox + agent\u2026${C5.reset}
73537
+ `
73538
+ );
73228
73539
  }
73229
73540
  const agent = await createAgent2();
73230
73541
  const sys = agent.getSystemPrompt();
@@ -73252,11 +73563,17 @@ async function main() {
73252
73563
  `
73253
73564
  );
73254
73565
  const COMMANDS = [
73255
- { name: "/model", desc: "trocar o modelo (setas; OpenRouter c/ busca)" },
73566
+ {
73567
+ name: "/model",
73568
+ desc: "trocar o modelo (setas; NVIDIA/OpenRouter c/ busca)"
73569
+ },
73256
73570
  { name: "/api", desc: "trocar chave de API (NVIDIA / OpenRouter)" },
73257
73571
  { name: "/skills", desc: "listar as skills instaladas" },
73258
73572
  { name: "/skillcreator", desc: "criar uma nova skill" },
73259
- { name: "/system", desc: "salvar o system prompt (HTML) na \xC1rea de Trabalho" },
73573
+ {
73574
+ name: "/system",
73575
+ desc: "salvar o system prompt (HTML) na \xC1rea de Trabalho"
73576
+ },
73260
73577
  { name: "/nov", desc: "novidades desta vers\xE3o" },
73261
73578
  { name: "/exit", desc: "fechar o clawfast" }
73262
73579
  ];
@@ -73288,13 +73605,13 @@ async function main() {
73288
73605
  const desktopDir = () => {
73289
73606
  const home = import_node_os6.default.homedir();
73290
73607
  const candidates = [
73291
- process.env.OneDrive ? import_node_path11.default.join(process.env.OneDrive, "Desktop") : null,
73292
- process.env.USERPROFILE ? import_node_path11.default.join(process.env.USERPROFILE, "Desktop") : null,
73293
- import_node_path11.default.join(home, "Desktop"),
73294
- import_node_path11.default.join(home, "\xC1rea de Trabalho"),
73295
- import_node_path11.default.join(home, "OneDrive", "Desktop")
73608
+ process.env.OneDrive ? import_node_path12.default.join(process.env.OneDrive, "Desktop") : null,
73609
+ process.env.USERPROFILE ? import_node_path12.default.join(process.env.USERPROFILE, "Desktop") : null,
73610
+ import_node_path12.default.join(home, "Desktop"),
73611
+ import_node_path12.default.join(home, "\xC1rea de Trabalho"),
73612
+ import_node_path12.default.join(home, "OneDrive", "Desktop")
73296
73613
  ].filter((p) => Boolean(p));
73297
- return candidates.find((p) => (0, import_node_fs10.existsSync)(p)) ?? home;
73614
+ return candidates.find((p) => (0, import_node_fs11.existsSync)(p)) ?? home;
73298
73615
  };
73299
73616
  const printFatal = (err) => {
73300
73617
  process.stderr.write(
@@ -73360,8 +73677,10 @@ ${C5.cyan}nome da skill?${C5.reset} ${C5.dim}(ex: xss-recon)${C5.reset}
73360
73677
  }
73361
73678
  if (sub === "delete" || sub === "rm" || sub === "remove") {
73362
73679
  if (!arg) {
73363
- process.stdout.write(`${C5.yellow}uso: /skill delete <nome>${C5.reset}
73364
- `);
73680
+ process.stdout.write(
73681
+ `${C5.yellow}uso: /skill delete <nome>${C5.reset}
73682
+ `
73683
+ );
73365
73684
  return;
73366
73685
  }
73367
73686
  if (deleteSkill(arg)) {
@@ -73529,6 +73848,7 @@ ${C5.dim}ja disponivel para todos os modelos nesta sessao.${C5.reset}
73529
73848
  }
73530
73849
  ];
73531
73850
  const openRouterProvider = KEY_PROVIDERS.find((p) => p.id === "openrouter");
73851
+ const nvidiaProvider = KEY_PROVIDERS.find((p) => p.id === "nvidia");
73532
73852
  const promptAndSwapKey = async (provider, inlineKey = "") => {
73533
73853
  let raw = inlineKey;
73534
73854
  if (!raw) {
@@ -73555,8 +73875,10 @@ ${C5.dim}ja disponivel para todos os modelos nesta sessao.${C5.reset}
73555
73875
  );
73556
73876
  return false;
73557
73877
  }
73558
- process.stdout.write(`${C5.dim}testando a chave na ${provider.name}\u2026${C5.reset}
73559
- `);
73878
+ process.stdout.write(
73879
+ `${C5.dim}testando a chave na ${provider.name}\u2026${C5.reset}
73880
+ `
73881
+ );
73560
73882
  const test = await provider.test(key);
73561
73883
  if (!test.ok) {
73562
73884
  if (test.status === 401 || test.status === 403) {
@@ -73632,9 +73954,58 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
73632
73954
  printFatal(err);
73633
73955
  }
73634
73956
  };
73957
+ const openNvidiaPicker = async () => {
73958
+ if (!agent.isNvidiaAvailable()) {
73959
+ process.stdout.write(
73960
+ `${C5.dim}NVIDIA ainda sem chave. Vamos adicionar uma.${C5.reset}
73961
+ `
73962
+ );
73963
+ const ok = await promptAndSwapKey(nvidiaProvider);
73964
+ if (!ok) return;
73965
+ }
73966
+ process.stdout.write(`${C5.dim}buscando cat\xE1logo NVIDIA\u2026${C5.reset}
73967
+ `);
73968
+ let models;
73969
+ try {
73970
+ models = await agent.listNvidiaModels();
73971
+ } catch (err) {
73972
+ process.stdout.write(
73973
+ `${C5.red}\u2717 falha ao listar modelos NVIDIA: ${err instanceof Error ? err.message : String(err)}${C5.reset}
73974
+ `
73975
+ );
73976
+ return;
73977
+ }
73978
+ if (models.length === 0) {
73979
+ process.stdout.write(
73980
+ `${C5.yellow}\u26A0 a NVIDIA n\xE3o retornou nenhum modelo${C5.reset}
73981
+ `
73982
+ );
73983
+ return;
73984
+ }
73985
+ const items = models.map((m) => ({
73986
+ label: m.name,
73987
+ hint: m.id
73988
+ }));
73989
+ const choice2 = await inputUI.searchSelect("NVIDIA \u2014 buscar modelo", items, {
73990
+ placeholder: "digite para filtrar (nome ou slug)"
73991
+ });
73992
+ if (choice2 === null) {
73993
+ process.stdout.write(`${C5.dim}sele\xE7\xE3o de modelo cancelada${C5.reset}
73994
+ `);
73995
+ return;
73996
+ }
73997
+ try {
73998
+ printModelResult(
73999
+ await agent.setModelSelection(`nvidia:${models[choice2].id}`)
74000
+ );
74001
+ } catch (err) {
74002
+ printFatal(err);
74003
+ }
74004
+ };
73635
74005
  const openModelSelector = async () => {
73636
74006
  const state = agent.getModelSelection();
73637
74007
  const orAvailable = agent.isOpenRouterAvailable();
74008
+ const nvAvailable = agent.isNvidiaAvailable();
73638
74009
  const items = [
73639
74010
  {
73640
74011
  label: "Auto \u2014 cadeia de fallback autom\xE1tica",
@@ -73642,13 +74013,19 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
73642
74013
  },
73643
74014
  ...state.chain.map((c) => ({ label: c.label, hint: c.key }))
73644
74015
  ];
74016
+ const nvidiaIdx = items.length;
74017
+ items.push({
74018
+ label: "NVIDIA \u2014 buscar no cat\xE1logo completo",
74019
+ hint: nvAvailable ? "lista todos os modelos da NVIDIA build" : "pede a API key e lista o cat\xE1logo"
74020
+ });
73645
74021
  const openRouterIdx = items.length;
73646
74022
  items.push({
73647
74023
  label: "OpenRouter \u2014 buscar no cat\xE1logo completo",
73648
74024
  hint: orAvailable ? "lista todos os modelos da sua conta" : "pede a API key e lista o cat\xE1logo"
73649
74025
  });
73650
74026
  const activeIsOpenRouter = state.activeModelKey.startsWith("openrouter:");
73651
- const activeIdx = state.mode === "auto" ? 0 : activeIsOpenRouter ? openRouterIdx : Math.max(
74027
+ const activeIsNvidiaDynamic = state.activeModelKey.startsWith("nvidia:");
74028
+ const activeIdx = state.mode === "auto" ? 0 : activeIsNvidiaDynamic ? nvidiaIdx : activeIsOpenRouter ? openRouterIdx : Math.max(
73652
74029
  0,
73653
74030
  1 + state.chain.findIndex((c) => c.key === state.activeModelKey)
73654
74031
  );
@@ -73658,6 +74035,10 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
73658
74035
  `);
73659
74036
  return;
73660
74037
  }
74038
+ if (choice2 === nvidiaIdx) {
74039
+ await openNvidiaPicker();
74040
+ return;
74041
+ }
73661
74042
  if (choice2 === openRouterIdx) {
73662
74043
  await openOpenRouterPicker();
73663
74044
  return;
@@ -73720,6 +74101,10 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
73720
74101
  await openOpenRouterPicker();
73721
74102
  return;
73722
74103
  }
74104
+ if (arg.toLowerCase() === "nvidia" || arg.toLowerCase() === "nv") {
74105
+ await openNvidiaPicker();
74106
+ return;
74107
+ }
73723
74108
  try {
73724
74109
  printModelResult(await agent.setModelSelection(arg));
73725
74110
  } catch (err) {
@@ -73731,7 +74116,7 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
73731
74116
  const sysText = agent.getSystemPrompt();
73732
74117
  const audit = agent.getSystemPromptAudit();
73733
74118
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
73734
- const filePath = import_node_path11.default.join(
74119
+ const filePath = import_node_path12.default.join(
73735
74120
  desktopDir(),
73736
74121
  `clawfast-system-prompt_${stamp}.html`
73737
74122
  );
@@ -73853,13 +74238,13 @@ ${C5.dim}interrompido \u2014 Ctrl+C de novo para fechar${C5.reset}
73853
74238
  }
73854
74239
  await shutdown();
73855
74240
  }
73856
- var import_node_os6, import_node_path11, import_node_fs10, import_promises3, deepseekEnabled, configuredModelProviders;
74241
+ var import_node_os6, import_node_path12, import_node_fs11, import_promises3, deepseekEnabled, configuredModelProviders;
73857
74242
  var init_index = __esm({
73858
74243
  "index.ts"() {
73859
74244
  "use strict";
73860
74245
  import_node_os6 = __toESM(require("node:os"));
73861
- import_node_path11 = __toESM(require("node:path"));
73862
- import_node_fs10 = require("node:fs");
74246
+ import_node_path12 = __toESM(require("node:path"));
74247
+ import_node_fs11 = require("node:fs");
73863
74248
  import_promises3 = require("node:fs/promises");
73864
74249
  init_paste_input();
73865
74250
  init_boot_ui();