@probelabs/probe 0.6.0-rc169 → 0.6.0-rc170

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cjs/index.cjs CHANGED
@@ -12522,6 +12522,50 @@ var init_ConfigurableSerdeContext = __esm({
12522
12522
  }
12523
12523
  });
12524
12524
 
12525
+ // node_modules/@aws-sdk/core/dist-es/submodules/protocols/structIterator.js
12526
+ function* serializingStructIterator(ns, sourceObject) {
12527
+ if (ns.isUnitSchema()) {
12528
+ return;
12529
+ }
12530
+ const struct2 = ns.getSchema();
12531
+ for (let i4 = 0; i4 < struct2[4].length; ++i4) {
12532
+ const key = struct2[4][i4];
12533
+ const memberNs = new NormalizedSchema([struct2[5][i4], 0], key);
12534
+ if (!(key in sourceObject) && !memberNs.isIdempotencyToken()) {
12535
+ continue;
12536
+ }
12537
+ yield [key, memberNs];
12538
+ }
12539
+ }
12540
+ function* deserializingStructIterator(ns, sourceObject, nameTrait) {
12541
+ if (ns.isUnitSchema()) {
12542
+ return;
12543
+ }
12544
+ const struct2 = ns.getSchema();
12545
+ let keysRemaining = Object.keys(sourceObject).length;
12546
+ for (let i4 = 0; i4 < struct2[4].length; ++i4) {
12547
+ if (keysRemaining === 0) {
12548
+ break;
12549
+ }
12550
+ const key = struct2[4][i4];
12551
+ const memberNs = new NormalizedSchema([struct2[5][i4], 0], key);
12552
+ let serializationKey = key;
12553
+ if (nameTrait) {
12554
+ serializationKey = memberNs.getMergedTraits()[nameTrait] ?? key;
12555
+ }
12556
+ if (!(serializationKey in sourceObject)) {
12557
+ continue;
12558
+ }
12559
+ yield [key, memberNs];
12560
+ keysRemaining -= 1;
12561
+ }
12562
+ }
12563
+ var init_structIterator = __esm({
12564
+ "node_modules/@aws-sdk/core/dist-es/submodules/protocols/structIterator.js"() {
12565
+ init_schema();
12566
+ }
12567
+ });
12568
+
12525
12569
  // node_modules/@aws-sdk/core/dist-es/submodules/protocols/json/jsonReviver.js
12526
12570
  function jsonReviver(key, value, context) {
12527
12571
  if (context?.source) {
@@ -12624,6 +12668,7 @@ var init_JsonShapeDeserializer = __esm({
12624
12668
  init_serde();
12625
12669
  import_util_base644 = __toESM(require_dist_cjs12());
12626
12670
  init_ConfigurableSerdeContext();
12671
+ init_structIterator();
12627
12672
  init_jsonReviver();
12628
12673
  init_parseJsonBody();
12629
12674
  JsonShapeDeserializer = class extends SerdeContextConfig {
@@ -12663,7 +12708,7 @@ var init_JsonShapeDeserializer = __esm({
12663
12708
  return out;
12664
12709
  } else if (ns.isStructSchema() && isObject2) {
12665
12710
  const out = {};
12666
- for (const [memberName, memberSchema] of ns.structIterator()) {
12711
+ for (const [memberName, memberSchema] of deserializingStructIterator(ns, value, this.settings.jsonName ? "jsonName" : false)) {
12667
12712
  const fromKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
12668
12713
  const deserializedValue = this._read(memberSchema, value[fromKey]);
12669
12714
  if (deserializedValue != null) {
@@ -12802,6 +12847,7 @@ var init_JsonShapeSerializer = __esm({
12802
12847
  init_serde();
12803
12848
  import_util_base645 = __toESM(require_dist_cjs12());
12804
12849
  init_ConfigurableSerdeContext();
12850
+ init_structIterator();
12805
12851
  init_jsonReplacer();
12806
12852
  JsonShapeSerializer = class extends SerdeContextConfig {
12807
12853
  settings;
@@ -12855,10 +12901,10 @@ var init_JsonShapeSerializer = __esm({
12855
12901
  return out;
12856
12902
  } else if (ns.isStructSchema() && isObject2) {
12857
12903
  const out = {};
12858
- for (const [memberName, memberSchema] of ns.structIterator()) {
12859
- const targetKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
12904
+ for (const [memberName, memberSchema] of serializingStructIterator(ns, value)) {
12860
12905
  const serializableValue = this._write(memberSchema, value[memberName], ns);
12861
12906
  if (serializableValue !== void 0) {
12907
+ const targetKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
12862
12908
  out[targetKey] = serializableValue;
12863
12909
  }
12864
12910
  }
@@ -14275,6 +14321,7 @@ var init_QueryShapeSerializer = __esm({
14275
14321
  import_smithy_client5 = __toESM(require_dist_cjs27());
14276
14322
  import_util_base646 = __toESM(require_dist_cjs12());
14277
14323
  init_ConfigurableSerdeContext();
14324
+ init_structIterator();
14278
14325
  QueryShapeSerializer = class extends SerdeContextConfig {
14279
14326
  settings;
14280
14327
  buffer;
@@ -14374,7 +14421,7 @@ var init_QueryShapeSerializer = __esm({
14374
14421
  }
14375
14422
  } else if (ns.isStructSchema()) {
14376
14423
  if (value && typeof value === "object") {
14377
- for (const [memberName, member2] of ns.structIterator()) {
14424
+ for (const [memberName, member2] of serializingStructIterator(ns, value)) {
14378
14425
  if (value[memberName] == null && !member2.isIdempotencyToken()) {
14379
14426
  continue;
14380
14427
  }
@@ -14650,6 +14697,7 @@ var init_XmlShapeSerializer = __esm({
14650
14697
  import_smithy_client7 = __toESM(require_dist_cjs27());
14651
14698
  import_util_base647 = __toESM(require_dist_cjs12());
14652
14699
  init_ConfigurableSerdeContext();
14700
+ init_structIterator();
14653
14701
  XmlShapeSerializer = class extends SerdeContextConfig {
14654
14702
  settings;
14655
14703
  stringBuffer;
@@ -14701,7 +14749,7 @@ var init_XmlShapeSerializer = __esm({
14701
14749
  }
14702
14750
  const structXmlNode = import_xml_builder3.XmlNode.of(name14);
14703
14751
  const [xmlnsAttr, xmlns] = this.getXmlnsAttribute(ns, parentXmlns);
14704
- for (const [memberName, memberSchema] of ns.structIterator()) {
14752
+ for (const [memberName, memberSchema] of serializingStructIterator(ns, value)) {
14705
14753
  const val = value[memberName];
14706
14754
  if (val != null || memberSchema.isIdempotencyToken()) {
14707
14755
  if (memberSchema.getMergedTraits().xmlAttribute) {
@@ -18445,7 +18493,7 @@ var require_package2 = __commonJS({
18445
18493
  module2.exports = {
18446
18494
  name: "@aws-sdk/client-bedrock-runtime",
18447
18495
  description: "AWS SDK for JavaScript Bedrock Runtime Client for Node.js, Browser and React Native",
18448
- version: "3.938.0",
18496
+ version: "3.940.0",
18449
18497
  scripts: {
18450
18498
  build: "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types'",
18451
18499
  "build:cjs": "node ../../scripts/compilation/inline client-bedrock-runtime",
@@ -18464,21 +18512,21 @@ var require_package2 = __commonJS({
18464
18512
  dependencies: {
18465
18513
  "@aws-crypto/sha256-browser": "5.2.0",
18466
18514
  "@aws-crypto/sha256-js": "5.2.0",
18467
- "@aws-sdk/core": "3.936.0",
18468
- "@aws-sdk/credential-provider-node": "3.936.0",
18515
+ "@aws-sdk/core": "3.940.0",
18516
+ "@aws-sdk/credential-provider-node": "3.940.0",
18469
18517
  "@aws-sdk/eventstream-handler-node": "3.936.0",
18470
18518
  "@aws-sdk/middleware-eventstream": "3.936.0",
18471
18519
  "@aws-sdk/middleware-host-header": "3.936.0",
18472
18520
  "@aws-sdk/middleware-logger": "3.936.0",
18473
18521
  "@aws-sdk/middleware-recursion-detection": "3.936.0",
18474
- "@aws-sdk/middleware-user-agent": "3.936.0",
18522
+ "@aws-sdk/middleware-user-agent": "3.940.0",
18475
18523
  "@aws-sdk/middleware-websocket": "3.936.0",
18476
18524
  "@aws-sdk/region-config-resolver": "3.936.0",
18477
- "@aws-sdk/token-providers": "3.936.0",
18525
+ "@aws-sdk/token-providers": "3.940.0",
18478
18526
  "@aws-sdk/types": "3.936.0",
18479
18527
  "@aws-sdk/util-endpoints": "3.936.0",
18480
18528
  "@aws-sdk/util-user-agent-browser": "3.936.0",
18481
- "@aws-sdk/util-user-agent-node": "3.936.0",
18529
+ "@aws-sdk/util-user-agent-node": "3.940.0",
18482
18530
  "@smithy/config-resolver": "^4.4.3",
18483
18531
  "@smithy/core": "^3.18.5",
18484
18532
  "@smithy/eventstream-serde-browser": "^4.2.5",
@@ -19225,7 +19273,7 @@ var init_package = __esm({
19225
19273
  "node_modules/@aws-sdk/nested-clients/package.json"() {
19226
19274
  package_default = {
19227
19275
  name: "@aws-sdk/nested-clients",
19228
- version: "3.936.0",
19276
+ version: "3.940.0",
19229
19277
  description: "Nested clients for AWS SDK packages.",
19230
19278
  main: "./dist-cjs/index.js",
19231
19279
  module: "./dist-es/index.js",
@@ -19254,16 +19302,16 @@ var init_package = __esm({
19254
19302
  dependencies: {
19255
19303
  "@aws-crypto/sha256-browser": "5.2.0",
19256
19304
  "@aws-crypto/sha256-js": "5.2.0",
19257
- "@aws-sdk/core": "3.936.0",
19305
+ "@aws-sdk/core": "3.940.0",
19258
19306
  "@aws-sdk/middleware-host-header": "3.936.0",
19259
19307
  "@aws-sdk/middleware-logger": "3.936.0",
19260
19308
  "@aws-sdk/middleware-recursion-detection": "3.936.0",
19261
- "@aws-sdk/middleware-user-agent": "3.936.0",
19309
+ "@aws-sdk/middleware-user-agent": "3.940.0",
19262
19310
  "@aws-sdk/region-config-resolver": "3.936.0",
19263
19311
  "@aws-sdk/types": "3.936.0",
19264
19312
  "@aws-sdk/util-endpoints": "3.936.0",
19265
19313
  "@aws-sdk/util-user-agent-browser": "3.936.0",
19266
- "@aws-sdk/util-user-agent-node": "3.936.0",
19314
+ "@aws-sdk/util-user-agent-node": "3.940.0",
19267
19315
  "@smithy/config-resolver": "^4.4.3",
19268
19316
  "@smithy/core": "^3.18.5",
19269
19317
  "@smithy/fetch-http-handler": "^5.3.6",
@@ -20655,7 +20703,7 @@ var require_package3 = __commonJS({
20655
20703
  module2.exports = {
20656
20704
  name: "@aws-sdk/client-sso",
20657
20705
  description: "AWS SDK for JavaScript Sso Client for Node.js, Browser and React Native",
20658
- version: "3.936.0",
20706
+ version: "3.940.0",
20659
20707
  scripts: {
20660
20708
  build: "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types'",
20661
20709
  "build:cjs": "node ../../scripts/compilation/inline client-sso",
@@ -20674,16 +20722,16 @@ var require_package3 = __commonJS({
20674
20722
  dependencies: {
20675
20723
  "@aws-crypto/sha256-browser": "5.2.0",
20676
20724
  "@aws-crypto/sha256-js": "5.2.0",
20677
- "@aws-sdk/core": "3.936.0",
20725
+ "@aws-sdk/core": "3.940.0",
20678
20726
  "@aws-sdk/middleware-host-header": "3.936.0",
20679
20727
  "@aws-sdk/middleware-logger": "3.936.0",
20680
20728
  "@aws-sdk/middleware-recursion-detection": "3.936.0",
20681
- "@aws-sdk/middleware-user-agent": "3.936.0",
20729
+ "@aws-sdk/middleware-user-agent": "3.940.0",
20682
20730
  "@aws-sdk/region-config-resolver": "3.936.0",
20683
20731
  "@aws-sdk/types": "3.936.0",
20684
20732
  "@aws-sdk/util-endpoints": "3.936.0",
20685
20733
  "@aws-sdk/util-user-agent-browser": "3.936.0",
20686
- "@aws-sdk/util-user-agent-node": "3.936.0",
20734
+ "@aws-sdk/util-user-agent-node": "3.940.0",
20687
20735
  "@smithy/config-resolver": "^4.4.3",
20688
20736
  "@smithy/core": "^3.18.5",
20689
20737
  "@smithy/fetch-http-handler": "^5.3.6",
@@ -29493,7 +29541,27 @@ var init_HookManager = __esm({
29493
29541
  });
29494
29542
 
29495
29543
  // src/agent/imageConfig.js
29496
- var SUPPORTED_IMAGE_EXTENSIONS, IMAGE_MIME_TYPES;
29544
+ function isFormatSupportedByProvider(extension, provider) {
29545
+ if (!extension || typeof extension !== "string") {
29546
+ return false;
29547
+ }
29548
+ if (extension.includes("/") || extension.includes("\\") || extension.includes("..")) {
29549
+ return false;
29550
+ }
29551
+ const ext2 = extension.toLowerCase();
29552
+ if (!SUPPORTED_IMAGE_EXTENSIONS.includes(ext2)) {
29553
+ return false;
29554
+ }
29555
+ if (!provider || typeof provider !== "string") {
29556
+ return true;
29557
+ }
29558
+ const unsupportedFormats = PROVIDER_UNSUPPORTED_FORMATS[provider];
29559
+ if (unsupportedFormats && unsupportedFormats.includes(ext2)) {
29560
+ return false;
29561
+ }
29562
+ return true;
29563
+ }
29564
+ var SUPPORTED_IMAGE_EXTENSIONS, IMAGE_MIME_TYPES, PROVIDER_UNSUPPORTED_FORMATS;
29497
29565
  var init_imageConfig = __esm({
29498
29566
  "src/agent/imageConfig.js"() {
29499
29567
  "use strict";
@@ -29506,6 +29574,10 @@ var init_imageConfig = __esm({
29506
29574
  "bmp": "image/bmp",
29507
29575
  "svg": "image/svg+xml"
29508
29576
  };
29577
+ PROVIDER_UNSUPPORTED_FORMATS = {
29578
+ "google": ["svg"]
29579
+ // Google Gemini doesn't support image/svg+xml
29580
+ };
29509
29581
  }
29510
29582
  });
29511
29583
 
@@ -39255,7 +39327,7 @@ var init_esm4 = __esm({
39255
39327
  *
39256
39328
  * @internal
39257
39329
  */
39258
- constructor(cwd = process.cwd(), pathImpl, sep3, { nocase, childrenCacheSize = 16 * 1024, fs: fs8 = defaultFS } = {}) {
39330
+ constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs8 = defaultFS } = {}) {
39259
39331
  this.#fs = fsFromOption(fs8);
39260
39332
  if (cwd instanceof URL || cwd.startsWith("file://")) {
39261
39333
  cwd = (0, import_node_url.fileURLToPath)(cwd);
@@ -39266,7 +39338,7 @@ var init_esm4 = __esm({
39266
39338
  this.#resolveCache = new ResolveCache();
39267
39339
  this.#resolvePosixCache = new ResolveCache();
39268
39340
  this.#children = new ChildrenCache(childrenCacheSize);
39269
- const split = cwdPath.substring(this.rootPath.length).split(sep3);
39341
+ const split = cwdPath.substring(this.rootPath.length).split(sep4);
39270
39342
  if (split.length === 1 && !split[0]) {
39271
39343
  split.pop();
39272
39344
  }
@@ -59882,7 +59954,7 @@ var init_graph_builder = __esm({
59882
59954
  applyLinkStyles() {
59883
59955
  if (!this.pendingLinkStyles.length || !this.edges.length)
59884
59956
  return;
59885
- const normalize2 = (s4) => {
59957
+ const normalize3 = (s4) => {
59886
59958
  const out = {};
59887
59959
  for (const [kRaw, vRaw] of Object.entries(s4)) {
59888
59960
  const k4 = kRaw.trim().toLowerCase();
@@ -59903,7 +59975,7 @@ var init_graph_builder = __esm({
59903
59975
  return out;
59904
59976
  };
59905
59977
  for (const cmd of this.pendingLinkStyles) {
59906
- const style = normalize2(cmd.props);
59978
+ const style = normalize3(cmd.props);
59907
59979
  for (const idx of cmd.indices) {
59908
59980
  if (idx >= 0 && idx < this.edges.length) {
59909
59981
  const e4 = this.edges[idx];
@@ -66995,7 +67067,7 @@ var require_bk = __commonJS({
66995
67067
  return xs;
66996
67068
  }
66997
67069
  function buildBlockGraph(g4, layering, root2, reverseSep) {
66998
- var blockGraph = new Graph(), graphLabel = g4.graph(), sepFn = sep3(graphLabel.nodesep, graphLabel.edgesep, reverseSep);
67070
+ var blockGraph = new Graph(), graphLabel = g4.graph(), sepFn = sep4(graphLabel.nodesep, graphLabel.edgesep, reverseSep);
66999
67071
  _.forEach(layering, function(layer) {
67000
67072
  var u4;
67001
67073
  _.forEach(layer, function(v4) {
@@ -67085,7 +67157,7 @@ var require_bk = __commonJS({
67085
67157
  alignCoordinates(xss, smallestWidth);
67086
67158
  return balance(xss, g4.graph().align);
67087
67159
  }
67088
- function sep3(nodeSep, edgeSep, reverseSep) {
67160
+ function sep4(nodeSep, edgeSep, reverseSep) {
67089
67161
  return function(g4, v4, w4) {
67090
67162
  var vLabel = g4.node(v4);
67091
67163
  var wLabel = g4.node(w4);
@@ -67170,7 +67242,7 @@ var require_layout = __commonJS({
67170
67242
  "use strict";
67171
67243
  var _ = require_lodash2();
67172
67244
  var acyclic = require_acyclic();
67173
- var normalize2 = require_normalize();
67245
+ var normalize3 = require_normalize();
67174
67246
  var rank = require_rank();
67175
67247
  var normalizeRanks = require_util().normalizeRanks;
67176
67248
  var parentDummyChains = require_parent_dummy_chains();
@@ -67232,7 +67304,7 @@ var require_layout = __commonJS({
67232
67304
  removeEdgeLabelProxies(g4);
67233
67305
  });
67234
67306
  time2(" normalize.run", function() {
67235
- normalize2.run(g4);
67307
+ normalize3.run(g4);
67236
67308
  });
67237
67309
  time2(" parentDummyChains", function() {
67238
67310
  parentDummyChains(g4);
@@ -67259,7 +67331,7 @@ var require_layout = __commonJS({
67259
67331
  removeBorderNodes(g4);
67260
67332
  });
67261
67333
  time2(" normalize.undo", function() {
67262
- normalize2.undo(g4);
67334
+ normalize3.undo(g4);
67263
67335
  });
67264
67336
  time2(" fixupEdgeLabelCoords", function() {
67265
67337
  fixupEdgeLabelCoords(g4);
@@ -73630,8 +73702,8 @@ var require_resolve = __commonJS({
73630
73702
  }
73631
73703
  return count;
73632
73704
  }
73633
- function getFullPath(resolver, id = "", normalize2) {
73634
- if (normalize2 !== false)
73705
+ function getFullPath(resolver, id = "", normalize3) {
73706
+ if (normalize3 !== false)
73635
73707
  id = normalizeId(id);
73636
73708
  const p4 = resolver.parse(id);
73637
73709
  return _getFullPath(resolver, p4);
@@ -74971,7 +75043,7 @@ var require_fast_uri = __commonJS({
74971
75043
  "use strict";
74972
75044
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
74973
75045
  var { SCHEMES, getSchemeHandler } = require_schemes();
74974
- function normalize2(uri, options) {
75046
+ function normalize3(uri, options) {
74975
75047
  if (typeof uri === "string") {
74976
75048
  uri = /** @type {T} */
74977
75049
  serialize(parse6(uri, options), options);
@@ -75207,7 +75279,7 @@ var require_fast_uri = __commonJS({
75207
75279
  }
75208
75280
  var fastUri = {
75209
75281
  SCHEMES,
75210
- normalize: normalize2,
75282
+ normalize: normalize3,
75211
75283
  resolve: resolve5,
75212
75284
  resolveComponent,
75213
75285
  equal,
@@ -82900,6 +82972,14 @@ var init_ProbeAgent = __esm({
82900
82972
  if (!imagePath) {
82901
82973
  throw new Error("Image path is required");
82902
82974
  }
82975
+ const filename = (0, import_path7.basename)(imagePath);
82976
+ const extension = filename.toLowerCase().split(".").pop();
82977
+ if (!extension || !SUPPORTED_IMAGE_EXTENSIONS.includes(extension)) {
82978
+ throw new Error(`Invalid or unsupported image extension: ${extension}. Supported formats: ${SUPPORTED_IMAGE_EXTENSIONS.join(", ")}`);
82979
+ }
82980
+ if (this.apiType && !isFormatSupportedByProvider(extension, this.apiType)) {
82981
+ throw new Error(`Image format '${extension}' is not supported by the current AI provider (${this.apiType}). Try using a different image format like PNG or JPEG.`);
82982
+ }
82903
82983
  const loaded = await this.loadImageIfValid(imagePath);
82904
82984
  if (!loaded) {
82905
82985
  throw new Error(`Failed to load image: ${imagePath}. The file may not exist, be too large, have an unsupported format, or be outside allowed directories.`);
@@ -83518,12 +83598,16 @@ var init_ProbeAgent = __esm({
83518
83598
  let absolutePath;
83519
83599
  let isPathAllowed2 = false;
83520
83600
  if ((0, import_path7.isAbsolute)(imagePath)) {
83521
- absolutePath = imagePath;
83522
- isPathAllowed2 = allowedDirs.some((dir) => absolutePath.startsWith((0, import_path7.resolve)(dir)));
83601
+ absolutePath = (0, import_path7.normalize)((0, import_path7.resolve)(imagePath));
83602
+ isPathAllowed2 = allowedDirs.some((dir) => {
83603
+ const normalizedDir = (0, import_path7.normalize)((0, import_path7.resolve)(dir));
83604
+ return absolutePath === normalizedDir || absolutePath.startsWith(normalizedDir + import_path7.sep);
83605
+ });
83523
83606
  } else {
83524
83607
  for (const dir of allowedDirs) {
83525
- const resolvedPath2 = (0, import_path7.resolve)(dir, imagePath);
83526
- if (resolvedPath2.startsWith((0, import_path7.resolve)(dir))) {
83608
+ const normalizedDir = (0, import_path7.normalize)((0, import_path7.resolve)(dir));
83609
+ const resolvedPath2 = (0, import_path7.normalize)((0, import_path7.resolve)(dir, imagePath));
83610
+ if (resolvedPath2 === normalizedDir || resolvedPath2.startsWith(normalizedDir + import_path7.sep)) {
83527
83611
  absolutePath = resolvedPath2;
83528
83612
  isPathAllowed2 = true;
83529
83613
  break;
@@ -84531,8 +84615,8 @@ ${toolResultContent}
84531
84615
  // Pass parent session ID for tracking
84532
84616
  path: this.searchPath,
84533
84617
  // Inherit search path
84534
- provider: this.provider,
84535
- // Inherit AI provider
84618
+ provider: this.apiType,
84619
+ // Inherit AI provider (string identifier)
84536
84620
  model: this.model,
84537
84621
  // Inherit model
84538
84622
  debug: this.debug,
@@ -84541,7 +84625,7 @@ ${toolResultContent}
84541
84625
  if (this.debug) {
84542
84626
  console.log(`[DEBUG] Executing delegate tool at iteration ${currentIteration}/${maxIterations}`);
84543
84627
  console.log(`[DEBUG] Parent session: ${this.sessionId}`);
84544
- console.log(`[DEBUG] Inherited config: path=${this.searchPath}, provider=${this.provider}, model=${this.model}`);
84628
+ console.log(`[DEBUG] Inherited config: path=${this.searchPath}, provider=${this.apiType}, model=${this.model}`);
84545
84629
  console.log(`[DEBUG] Delegate task: ${toolParams.task?.substring(0, 100)}...`);
84546
84630
  }
84547
84631
  if (this.tracer) {
@@ -84608,9 +84692,6 @@ ${toolResultContent}
84608
84692
  role: "user",
84609
84693
  content: toolResultMessage
84610
84694
  });
84611
- if (toolResultContent) {
84612
- await this.processImageReferences(toolResultContent);
84613
- }
84614
84695
  if (this.debug) {
84615
84696
  console.log(`[DEBUG] Tool ${toolName} executed successfully. Result length: ${typeof toolResult === "string" ? toolResult.length : JSON.stringify(toolResult).length}`);
84616
84697
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc169",
3
+ "version": "0.6.0-rc170",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -13,11 +13,11 @@ import { randomUUID } from 'crypto';
13
13
  import { EventEmitter } from 'events';
14
14
  import { existsSync } from 'fs';
15
15
  import { readFile, stat } from 'fs/promises';
16
- import { resolve, isAbsolute, dirname } from 'path';
16
+ import { resolve, isAbsolute, dirname, basename, normalize, sep } from 'path';
17
17
  import { TokenCounter } from './tokenCounter.js';
18
18
  import { InMemoryStorageAdapter } from './storage/InMemoryStorageAdapter.js';
19
19
  import { HookManager, HOOK_TYPES } from './hooks/HookManager.js';
20
- import { SUPPORTED_IMAGE_EXTENSIONS, IMAGE_MIME_TYPES } from './imageConfig.js';
20
+ import { SUPPORTED_IMAGE_EXTENSIONS, IMAGE_MIME_TYPES, isFormatSupportedByProvider } from './imageConfig.js';
21
21
  import {
22
22
  createTools,
23
23
  searchToolDefinition,
@@ -475,6 +475,21 @@ export class ProbeAgent {
475
475
  throw new Error('Image path is required');
476
476
  }
477
477
 
478
+ // Validate extension before attempting to load
479
+ // Use basename to prevent path traversal attacks (e.g., 'malicious.jpg/../../../etc/passwd')
480
+ const filename = basename(imagePath);
481
+ const extension = filename.toLowerCase().split('.').pop();
482
+
483
+ // Always validate extension is in allowed list (defense-in-depth)
484
+ if (!extension || !SUPPORTED_IMAGE_EXTENSIONS.includes(extension)) {
485
+ throw new Error(`Invalid or unsupported image extension: ${extension}. Supported formats: ${SUPPORTED_IMAGE_EXTENSIONS.join(', ')}`);
486
+ }
487
+
488
+ // Check provider-specific format restrictions (e.g., SVG not supported by Google Gemini)
489
+ if (this.apiType && !isFormatSupportedByProvider(extension, this.apiType)) {
490
+ throw new Error(`Image format '${extension}' is not supported by the current AI provider (${this.apiType}). Try using a different image format like PNG or JPEG.`);
491
+ }
492
+
478
493
  // Load the image using the existing loadImageIfValid method
479
494
  const loaded = await this.loadImageIfValid(imagePath);
480
495
 
@@ -1262,20 +1277,28 @@ export class ProbeAgent {
1262
1277
  }
1263
1278
 
1264
1279
  // Security validation: check if path is within any allowed directory
1280
+ // Use normalize() after resolve() to handle path traversal attempts (e.g., '/allowed/../etc/passwd')
1265
1281
  const allowedDirs = this.allowedFolders && this.allowedFolders.length > 0 ? this.allowedFolders : [process.cwd()];
1266
-
1282
+
1267
1283
  let absolutePath;
1268
1284
  let isPathAllowed = false;
1269
-
1285
+
1270
1286
  // If absolute path, check if it's within any allowed directory
1271
1287
  if (isAbsolute(imagePath)) {
1272
- absolutePath = imagePath;
1273
- isPathAllowed = allowedDirs.some(dir => absolutePath.startsWith(resolve(dir)));
1288
+ // Normalize to resolve any '..' sequences
1289
+ absolutePath = normalize(resolve(imagePath));
1290
+ isPathAllowed = allowedDirs.some(dir => {
1291
+ const normalizedDir = normalize(resolve(dir));
1292
+ // Ensure the path is within the allowed directory (add separator to prevent prefix attacks)
1293
+ return absolutePath === normalizedDir || absolutePath.startsWith(normalizedDir + sep);
1294
+ });
1274
1295
  } else {
1275
1296
  // For relative paths, try resolving against each allowed directory
1276
1297
  for (const dir of allowedDirs) {
1277
- const resolvedPath = resolve(dir, imagePath);
1278
- if (resolvedPath.startsWith(resolve(dir))) {
1298
+ const normalizedDir = normalize(resolve(dir));
1299
+ const resolvedPath = normalize(resolve(dir, imagePath));
1300
+ // Ensure the resolved path is within the allowed directory
1301
+ if (resolvedPath === normalizedDir || resolvedPath.startsWith(normalizedDir + sep)) {
1279
1302
  absolutePath = resolvedPath;
1280
1303
  isPathAllowed = true;
1281
1304
  break;
@@ -1319,6 +1342,10 @@ export class ProbeAgent {
1319
1342
  return false;
1320
1343
  }
1321
1344
 
1345
+ // Note: Provider-specific format validation (e.g., SVG not supported by Google Gemini)
1346
+ // is handled by the readImage tool which provides explicit error messages.
1347
+ // loadImageIfValid is a lower-level method that only checks general format support.
1348
+
1322
1349
  // Determine MIME type (from shared config)
1323
1350
  const mimeType = IMAGE_MIME_TYPES[extension];
1324
1351
 
@@ -2504,7 +2531,7 @@ When troubleshooting:
2504
2531
  maxIterations,
2505
2532
  parentSessionId: this.sessionId, // Pass parent session ID for tracking
2506
2533
  path: this.searchPath, // Inherit search path
2507
- provider: this.provider, // Inherit AI provider
2534
+ provider: this.apiType, // Inherit AI provider (string identifier)
2508
2535
  model: this.model, // Inherit model
2509
2536
  debug: this.debug,
2510
2537
  tracer: this.tracer
@@ -2513,7 +2540,7 @@ When troubleshooting:
2513
2540
  if (this.debug) {
2514
2541
  console.log(`[DEBUG] Executing delegate tool at iteration ${currentIteration}/${maxIterations}`);
2515
2542
  console.log(`[DEBUG] Parent session: ${this.sessionId}`);
2516
- console.log(`[DEBUG] Inherited config: path=${this.searchPath}, provider=${this.provider}, model=${this.model}`);
2543
+ console.log(`[DEBUG] Inherited config: path=${this.searchPath}, provider=${this.apiType}, model=${this.model}`);
2517
2544
  console.log(`[DEBUG] Delegate task: ${toolParams.task?.substring(0, 100)}...`);
2518
2545
  }
2519
2546
 
@@ -2597,10 +2624,10 @@ When troubleshooting:
2597
2624
  content: toolResultMessage
2598
2625
  });
2599
2626
 
2600
- // Process tool result for image references
2601
- if (toolResultContent) {
2602
- await this.processImageReferences(toolResultContent);
2603
- }
2627
+ // NOTE: Automatic image processing removed (GitHub issue #305)
2628
+ // Images are now only loaded when the AI explicitly calls the readImage tool
2629
+ // This prevents: 1) implicit behavior that users don't expect
2630
+ // 2) crashes with unsupported MIME types (e.g., SVG on Gemini)
2604
2631
 
2605
2632
  if (this.debug) {
2606
2633
  console.log(`[DEBUG] Tool ${toolName} executed successfully. Result length: ${typeof toolResult === 'string' ? toolResult.length : JSON.stringify(toolResult).length}`);
@@ -21,6 +21,12 @@ export const IMAGE_MIME_TYPES = {
21
21
  'svg': 'image/svg+xml'
22
22
  };
23
23
 
24
+ // Provider-specific unsupported image formats
25
+ // These providers do not support certain MIME types and will crash if they receive them
26
+ export const PROVIDER_UNSUPPORTED_FORMATS = {
27
+ 'google': ['svg'], // Google Gemini doesn't support image/svg+xml
28
+ };
29
+
24
30
  /**
25
31
  * Generate a regex pattern string for matching image file extensions
26
32
  * @param {string[]} extensions - Array of extensions (without dots)
@@ -38,3 +44,54 @@ export function getExtensionPattern(extensions = SUPPORTED_IMAGE_EXTENSIONS) {
38
44
  export function getMimeType(extension) {
39
45
  return IMAGE_MIME_TYPES[extension.toLowerCase()];
40
46
  }
47
+
48
+ /**
49
+ * Check if an image extension is supported by a specific provider
50
+ * @param {string} extension - File extension (without dot)
51
+ * @param {string} provider - Provider name (e.g., 'google', 'anthropic', 'openai')
52
+ * @returns {boolean} True if the format is supported by the provider
53
+ */
54
+ export function isFormatSupportedByProvider(extension, provider) {
55
+ // Validate extension parameter - must be a non-empty string without path separators
56
+ if (!extension || typeof extension !== 'string') {
57
+ return false;
58
+ }
59
+ // Sanitize: reject extensions containing path traversal characters
60
+ if (extension.includes('/') || extension.includes('\\') || extension.includes('..')) {
61
+ return false;
62
+ }
63
+
64
+ const ext = extension.toLowerCase();
65
+
66
+ // First check if it's a generally supported format
67
+ if (!SUPPORTED_IMAGE_EXTENSIONS.includes(ext)) {
68
+ return false;
69
+ }
70
+
71
+ // Handle null/undefined provider gracefully (treat as no restrictions)
72
+ if (!provider || typeof provider !== 'string') {
73
+ return true;
74
+ }
75
+
76
+ // Check provider-specific restrictions
77
+ const unsupportedFormats = PROVIDER_UNSUPPORTED_FORMATS[provider];
78
+ if (unsupportedFormats && unsupportedFormats.includes(ext)) {
79
+ return false;
80
+ }
81
+
82
+ return true;
83
+ }
84
+
85
+ /**
86
+ * Get supported image extensions for a specific provider
87
+ * @param {string} provider - Provider name (e.g., 'google', 'anthropic', 'openai')
88
+ * @returns {string[]} Array of supported extensions for this provider
89
+ */
90
+ export function getSupportedExtensionsForProvider(provider) {
91
+ // Handle null/undefined/non-string provider gracefully (return all extensions)
92
+ if (!provider || typeof provider !== 'string') {
93
+ return [...SUPPORTED_IMAGE_EXTENSIONS];
94
+ }
95
+ const unsupportedFormats = PROVIDER_UNSUPPORTED_FORMATS[provider] || [];
96
+ return SUPPORTED_IMAGE_EXTENSIONS.filter(ext => !unsupportedFormats.includes(ext));
97
+ }