@probelabs/probe 0.6.0-rc177 → 0.6.0-rc186

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.
@@ -2501,10 +2501,26 @@ When troubleshooting:
2501
2501
  // Execute native tool
2502
2502
  try {
2503
2503
  // Add sessionId and workingDirectory to params for tool execution
2504
+ // Validate and resolve workingDirectory
2505
+ let resolvedWorkingDirectory = (this.allowedFolders && this.allowedFolders[0]) || process.cwd();
2506
+ if (params.workingDirectory) {
2507
+ const requestedDir = resolve(params.workingDirectory);
2508
+ // Check if the requested directory is within allowed folders
2509
+ const isWithinAllowed = !this.allowedFolders || this.allowedFolders.length === 0 ||
2510
+ this.allowedFolders.some(folder => {
2511
+ const resolvedFolder = resolve(folder);
2512
+ return requestedDir === resolvedFolder || requestedDir.startsWith(resolvedFolder + sep);
2513
+ });
2514
+ if (isWithinAllowed) {
2515
+ resolvedWorkingDirectory = requestedDir;
2516
+ } else if (this.debug) {
2517
+ console.error(`[DEBUG] Rejected workingDirectory "${params.workingDirectory}" - not within allowed folders`);
2518
+ }
2519
+ }
2504
2520
  const toolParams = {
2505
2521
  ...params,
2506
2522
  sessionId: this.sessionId,
2507
- workingDirectory: (this.allowedFolders && this.allowedFolders[0]) || process.cwd()
2523
+ workingDirectory: resolvedWorkingDirectory
2508
2524
  };
2509
2525
 
2510
2526
  // Log tool execution in debug mode
@@ -159,6 +159,69 @@ export function isComplexCommand(command) {
159
159
  return result.isComplex;
160
160
  }
161
161
 
162
+ /**
163
+ * Check if a pattern is a complex pattern (contains shell operators)
164
+ * Complex patterns are used to match full command strings including operators
165
+ * @param {string} pattern - Pattern to check
166
+ * @returns {boolean} True if pattern contains shell operators
167
+ */
168
+ export function isComplexPattern(pattern) {
169
+ if (!pattern || typeof pattern !== 'string') return false;
170
+
171
+ // Check for operators in the pattern (aligned with complexPatterns in parseSimpleCommand)
172
+ const operatorPatterns = [
173
+ /\|/, // Pipes
174
+ /&&/, // Logical AND
175
+ /\|\|/, // Logical OR
176
+ /;/, // Command separator
177
+ /&$/, // Background execution
178
+ /\$\(/, // Command substitution $()
179
+ /`/, // Command substitution ``
180
+ />/, // Redirection >
181
+ /</, // Redirection <
182
+ ];
183
+
184
+ return operatorPatterns.some(p => p.test(pattern));
185
+ }
186
+
187
+ /**
188
+ * Convert a glob-style pattern to regex for matching
189
+ * Supports * as wildcard (matches any characters except operators)
190
+ * @param {string} pattern - Glob pattern
191
+ * @returns {RegExp} Compiled regex
192
+ */
193
+ function globToRegex(pattern) {
194
+ // Escape regex special characters except *
195
+ let escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
196
+ // Convert * to .*? (non-greedy match)
197
+ escaped = escaped.replace(/\*/g, '.*?');
198
+ // Make it match the full string
199
+ return new RegExp('^' + escaped + '$', 'i');
200
+ }
201
+
202
+ /**
203
+ * Match a command string against a complex pattern
204
+ * Complex patterns use glob-style wildcards (*) for matching
205
+ * @param {string} command - Full command string
206
+ * @param {string} pattern - Complex pattern with wildcards
207
+ * @returns {boolean} True if command matches the pattern
208
+ */
209
+ export function matchesComplexPattern(command, pattern) {
210
+ if (!command || !pattern) return false;
211
+
212
+ // Normalize whitespace
213
+ const normalizedCommand = command.trim().replace(/\s+/g, ' ');
214
+ const normalizedPattern = pattern.trim().replace(/\s+/g, ' ');
215
+
216
+ try {
217
+ const regex = globToRegex(normalizedPattern);
218
+ return regex.test(normalizedCommand);
219
+ } catch (e) {
220
+ // If regex fails, fall back to exact match
221
+ return normalizedCommand === normalizedPattern;
222
+ }
223
+ }
224
+
162
225
  /**
163
226
  * Legacy compatibility function - parses command for permission checking
164
227
  * @param {string} command - Command to parse
@@ -6,7 +6,7 @@
6
6
  import { spawn } from 'child_process';
7
7
  import { resolve, join } from 'path';
8
8
  import { existsSync } from 'fs';
9
- import { parseCommandForExecution } from './bashCommandUtils.js';
9
+ import { parseCommandForExecution, isComplexCommand } from './bashCommandUtils.js';
10
10
 
11
11
  /**
12
12
  * Execute a bash command with security controls
@@ -63,31 +63,46 @@ export async function executeBashCommand(command, options = {}) {
63
63
  ...env
64
64
  };
65
65
 
66
- // Parse command for shell execution
67
- // We use shell: false for security, so we need to parse manually
68
- const args = parseCommandForExecution(command);
69
- if (!args || args.length === 0) {
70
- resolve({
71
- success: false,
72
- error: 'Failed to parse command',
73
- stdout: '',
74
- stderr: '',
75
- exitCode: 1,
76
- command,
77
- workingDirectory: cwd,
78
- duration: Date.now() - startTime
79
- });
80
- return;
81
- }
66
+ // Check if this is a complex command (contains pipes, operators, etc.)
67
+ const isComplex = isComplexCommand(command);
82
68
 
83
- const [cmd, ...cmdArgs] = args;
69
+ let cmd, cmdArgs, useShell;
70
+
71
+ if (isComplex) {
72
+ // For complex commands, use sh -c to execute through shell
73
+ // This is only reached if the permission checker allowed the complex command
74
+ cmd = 'sh';
75
+ cmdArgs = ['-c', command];
76
+ useShell = false; // We explicitly use sh -c, not spawn's shell option
77
+ if (debug) {
78
+ console.log(`[BashExecutor] Complex command - using sh -c`);
79
+ }
80
+ } else {
81
+ // Parse simple command for direct execution
82
+ const args = parseCommandForExecution(command);
83
+ if (!args || args.length === 0) {
84
+ resolve({
85
+ success: false,
86
+ error: 'Failed to parse command',
87
+ stdout: '',
88
+ stderr: '',
89
+ exitCode: 1,
90
+ command,
91
+ workingDirectory: cwd,
92
+ duration: Date.now() - startTime
93
+ });
94
+ return;
95
+ }
96
+ [cmd, ...cmdArgs] = args;
97
+ useShell = false;
98
+ }
84
99
 
85
100
  // Spawn the process
86
101
  const child = spawn(cmd, cmdArgs, {
87
102
  cwd,
88
103
  env: processEnv,
89
104
  stdio: ['ignore', 'pipe', 'pipe'], // stdin ignored, capture stdout/stderr
90
- shell: false, // For security
105
+ shell: useShell, // false for security
91
106
  windowsHide: true
92
107
  });
93
108
 
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { DEFAULT_ALLOW_PATTERNS, DEFAULT_DENY_PATTERNS } from './bashDefaults.js';
7
- import { parseCommand, isComplexCommand } from './bashCommandUtils.js';
7
+ import { parseCommand, isComplexCommand, isComplexPattern, matchesComplexPattern } from './bashCommandUtils.js';
8
8
 
9
9
  /**
10
10
  * Check if a pattern matches a parsed command
@@ -125,7 +125,7 @@ export class BashPermissionChecker {
125
125
  }
126
126
 
127
127
  /**
128
- * Check if a simple command is allowed (rejects complex commands for security)
128
+ * Check if a simple command is allowed (complex commands allowed if they match patterns)
129
129
  * @param {string} command - Command to check
130
130
  * @returns {Object} Permission result
131
131
  */
@@ -138,19 +138,17 @@ export class BashPermissionChecker {
138
138
  };
139
139
  }
140
140
 
141
- // First check if this is a complex command - reject immediately for security
142
- if (isComplexCommand(command)) {
143
- return {
144
- allowed: false,
145
- reason: 'Complex shell commands with pipes, operators, or redirections are not supported for security reasons',
146
- command: command,
147
- isComplex: true
148
- };
141
+ // Check if this is a complex command
142
+ const commandIsComplex = isComplexCommand(command);
143
+
144
+ if (commandIsComplex) {
145
+ // For complex commands, check against complex patterns in allow/deny lists
146
+ return this._checkComplexCommand(command);
149
147
  }
150
148
 
151
149
  // Parse the simple command
152
150
  const parsed = parseCommand(command);
153
-
151
+
154
152
  if (parsed.error) {
155
153
  return {
156
154
  allowed: false,
@@ -203,14 +201,77 @@ export class BashPermissionChecker {
203
201
  parsed: parsed,
204
202
  isComplex: false
205
203
  };
206
-
204
+
207
205
  if (this.debug) {
208
206
  console.log(`[BashPermissions] ALLOWED - command passed all checks`);
209
207
  }
210
-
208
+
211
209
  return result;
212
210
  }
213
211
 
212
+ /**
213
+ * Check a complex command against complex patterns in allow/deny lists
214
+ * @private
215
+ * @param {string} command - Complex command to check
216
+ * @returns {Object} Permission result
217
+ */
218
+ _checkComplexCommand(command) {
219
+ if (this.debug) {
220
+ console.log(`[BashPermissions] Checking complex command: "${command}"`);
221
+ }
222
+
223
+ // Get complex patterns from allow and deny lists
224
+ const complexAllowPatterns = this.allowPatterns.filter(p => isComplexPattern(p));
225
+ const complexDenyPatterns = this.denyPatterns.filter(p => isComplexPattern(p));
226
+
227
+ if (this.debug) {
228
+ console.log(`[BashPermissions] Complex allow patterns: ${complexAllowPatterns.length}`);
229
+ console.log(`[BashPermissions] Complex deny patterns: ${complexDenyPatterns.length}`);
230
+ }
231
+
232
+ // Check deny patterns first (deny takes precedence)
233
+ for (const pattern of complexDenyPatterns) {
234
+ if (matchesComplexPattern(command, pattern)) {
235
+ if (this.debug) {
236
+ console.log(`[BashPermissions] DENIED - matches complex deny pattern: ${pattern}`);
237
+ }
238
+ return {
239
+ allowed: false,
240
+ reason: `Command matches deny pattern: ${pattern}`,
241
+ command: command,
242
+ isComplex: true,
243
+ matchedPatterns: [pattern]
244
+ };
245
+ }
246
+ }
247
+
248
+ // Check allow patterns
249
+ for (const pattern of complexAllowPatterns) {
250
+ if (matchesComplexPattern(command, pattern)) {
251
+ if (this.debug) {
252
+ console.log(`[BashPermissions] ALLOWED - matches complex allow pattern: ${pattern}`);
253
+ }
254
+ return {
255
+ allowed: true,
256
+ command: command,
257
+ isComplex: true,
258
+ matchedPattern: pattern
259
+ };
260
+ }
261
+ }
262
+
263
+ // No matching complex pattern found - reject complex command
264
+ if (this.debug) {
265
+ console.log(`[BashPermissions] DENIED - no matching complex pattern found`);
266
+ }
267
+ return {
268
+ allowed: false,
269
+ reason: 'Complex shell commands require explicit allow patterns (e.g., "cd * && git *")',
270
+ command: command,
271
+ isComplex: true
272
+ };
273
+ }
274
+
214
275
  /**
215
276
  * Get configuration summary
216
277
  * @returns {Object} Configuration info
@@ -9136,6 +9136,46 @@ function isComplexCommand(command) {
9136
9136
  const result = parseSimpleCommand(command);
9137
9137
  return result.isComplex;
9138
9138
  }
9139
+ function isComplexPattern(pattern) {
9140
+ if (!pattern || typeof pattern !== "string") return false;
9141
+ const operatorPatterns = [
9142
+ /\|/,
9143
+ // Pipes
9144
+ /&&/,
9145
+ // Logical AND
9146
+ /\|\|/,
9147
+ // Logical OR
9148
+ /;/,
9149
+ // Command separator
9150
+ /&$/,
9151
+ // Background execution
9152
+ /\$\(/,
9153
+ // Command substitution $()
9154
+ /`/,
9155
+ // Command substitution ``
9156
+ />/,
9157
+ // Redirection >
9158
+ /</
9159
+ // Redirection <
9160
+ ];
9161
+ return operatorPatterns.some((p) => p.test(pattern));
9162
+ }
9163
+ function globToRegex(pattern) {
9164
+ let escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
9165
+ escaped = escaped.replace(/\*/g, ".*?");
9166
+ return new RegExp("^" + escaped + "$", "i");
9167
+ }
9168
+ function matchesComplexPattern(command, pattern) {
9169
+ if (!command || !pattern) return false;
9170
+ const normalizedCommand = command.trim().replace(/\s+/g, " ");
9171
+ const normalizedPattern = pattern.trim().replace(/\s+/g, " ");
9172
+ try {
9173
+ const regex = globToRegex(normalizedPattern);
9174
+ return regex.test(normalizedCommand);
9175
+ } catch (e) {
9176
+ return normalizedCommand === normalizedPattern;
9177
+ }
9178
+ }
9139
9179
  function parseCommand(command) {
9140
9180
  const result = parseSimpleCommand(command);
9141
9181
  if (!result.success) {
@@ -9250,7 +9290,7 @@ var init_bashPermissions = __esm({
9250
9290
  }
9251
9291
  }
9252
9292
  /**
9253
- * Check if a simple command is allowed (rejects complex commands for security)
9293
+ * Check if a simple command is allowed (complex commands allowed if they match patterns)
9254
9294
  * @param {string} command - Command to check
9255
9295
  * @returns {Object} Permission result
9256
9296
  */
@@ -9262,13 +9302,9 @@ var init_bashPermissions = __esm({
9262
9302
  command
9263
9303
  };
9264
9304
  }
9265
- if (isComplexCommand(command)) {
9266
- return {
9267
- allowed: false,
9268
- reason: "Complex shell commands with pipes, operators, or redirections are not supported for security reasons",
9269
- command,
9270
- isComplex: true
9271
- };
9305
+ const commandIsComplex = isComplexCommand(command);
9306
+ if (commandIsComplex) {
9307
+ return this._checkComplexCommand(command);
9272
9308
  }
9273
9309
  const parsed = parseCommand(command);
9274
9310
  if (parsed.error) {
@@ -9320,6 +9356,59 @@ var init_bashPermissions = __esm({
9320
9356
  }
9321
9357
  return result;
9322
9358
  }
9359
+ /**
9360
+ * Check a complex command against complex patterns in allow/deny lists
9361
+ * @private
9362
+ * @param {string} command - Complex command to check
9363
+ * @returns {Object} Permission result
9364
+ */
9365
+ _checkComplexCommand(command) {
9366
+ if (this.debug) {
9367
+ console.log(`[BashPermissions] Checking complex command: "${command}"`);
9368
+ }
9369
+ const complexAllowPatterns = this.allowPatterns.filter((p) => isComplexPattern(p));
9370
+ const complexDenyPatterns = this.denyPatterns.filter((p) => isComplexPattern(p));
9371
+ if (this.debug) {
9372
+ console.log(`[BashPermissions] Complex allow patterns: ${complexAllowPatterns.length}`);
9373
+ console.log(`[BashPermissions] Complex deny patterns: ${complexDenyPatterns.length}`);
9374
+ }
9375
+ for (const pattern of complexDenyPatterns) {
9376
+ if (matchesComplexPattern(command, pattern)) {
9377
+ if (this.debug) {
9378
+ console.log(`[BashPermissions] DENIED - matches complex deny pattern: ${pattern}`);
9379
+ }
9380
+ return {
9381
+ allowed: false,
9382
+ reason: `Command matches deny pattern: ${pattern}`,
9383
+ command,
9384
+ isComplex: true,
9385
+ matchedPatterns: [pattern]
9386
+ };
9387
+ }
9388
+ }
9389
+ for (const pattern of complexAllowPatterns) {
9390
+ if (matchesComplexPattern(command, pattern)) {
9391
+ if (this.debug) {
9392
+ console.log(`[BashPermissions] ALLOWED - matches complex allow pattern: ${pattern}`);
9393
+ }
9394
+ return {
9395
+ allowed: true,
9396
+ command,
9397
+ isComplex: true,
9398
+ matchedPattern: pattern
9399
+ };
9400
+ }
9401
+ }
9402
+ if (this.debug) {
9403
+ console.log(`[BashPermissions] DENIED - no matching complex pattern found`);
9404
+ }
9405
+ return {
9406
+ allowed: false,
9407
+ reason: 'Complex shell commands require explicit allow patterns (e.g., "cd * && git *")',
9408
+ command,
9409
+ isComplex: true
9410
+ };
9411
+ }
9323
9412
  /**
9324
9413
  * Get configuration summary
9325
9414
  * @returns {Object} Configuration info
@@ -9378,28 +9467,40 @@ async function executeBashCommand(command, options = {}) {
9378
9467
  ...process.env,
9379
9468
  ...env
9380
9469
  };
9381
- const args = parseCommandForExecution(command);
9382
- if (!args || args.length === 0) {
9383
- resolve6({
9384
- success: false,
9385
- error: "Failed to parse command",
9386
- stdout: "",
9387
- stderr: "",
9388
- exitCode: 1,
9389
- command,
9390
- workingDirectory: cwd,
9391
- duration: Date.now() - startTime
9392
- });
9393
- return;
9470
+ const isComplex = isComplexCommand(command);
9471
+ let cmd, cmdArgs, useShell;
9472
+ if (isComplex) {
9473
+ cmd = "sh";
9474
+ cmdArgs = ["-c", command];
9475
+ useShell = false;
9476
+ if (debug) {
9477
+ console.log(`[BashExecutor] Complex command - using sh -c`);
9478
+ }
9479
+ } else {
9480
+ const args = parseCommandForExecution(command);
9481
+ if (!args || args.length === 0) {
9482
+ resolve6({
9483
+ success: false,
9484
+ error: "Failed to parse command",
9485
+ stdout: "",
9486
+ stderr: "",
9487
+ exitCode: 1,
9488
+ command,
9489
+ workingDirectory: cwd,
9490
+ duration: Date.now() - startTime
9491
+ });
9492
+ return;
9493
+ }
9494
+ [cmd, ...cmdArgs] = args;
9495
+ useShell = false;
9394
9496
  }
9395
- const [cmd, ...cmdArgs] = args;
9396
9497
  const child = spawn2(cmd, cmdArgs, {
9397
9498
  cwd,
9398
9499
  env: processEnv,
9399
9500
  stdio: ["ignore", "pipe", "pipe"],
9400
9501
  // stdin ignored, capture stdout/stderr
9401
- shell: false,
9402
- // For security
9502
+ shell: useShell,
9503
+ // false for security
9403
9504
  windowsHide: true
9404
9505
  });
9405
9506
  let stdout = "";
@@ -23000,6 +23101,16 @@ var init_regexp_parser = __esm({
23000
23101
  case "!":
23001
23102
  type = "NegativeLookahead";
23002
23103
  break;
23104
+ case "<": {
23105
+ switch (this.popChar()) {
23106
+ case "=":
23107
+ type = "Lookbehind";
23108
+ break;
23109
+ case "!":
23110
+ type = "NegativeLookbehind";
23111
+ }
23112
+ break;
23113
+ }
23003
23114
  }
23004
23115
  ASSERT_EXISTS(type);
23005
23116
  const disjunction = this.disjunction();
@@ -23491,9 +23602,9 @@ var init_regexp_parser = __esm({
23491
23602
  default:
23492
23603
  return false;
23493
23604
  }
23494
- // '(?=' or '(?!'
23605
+ // '(?=' or '(?!' or `(?<=` or `(?<!`
23495
23606
  case "(":
23496
- return this.peekChar(1) === "?" && (this.peekChar(2) === "=" || this.peekChar(2) === "!");
23607
+ return this.peekChar(1) === "?" && (this.peekChar(2) === "=" || this.peekChar(2) === "!" || this.peekChar(2) === "<" && (this.peekChar(3) === "=" || this.peekChar(3) === "!"));
23497
23608
  default:
23498
23609
  return false;
23499
23610
  }
@@ -23618,6 +23729,12 @@ var init_base_regexp_visitor = __esm({
23618
23729
  case "NegativeLookahead":
23619
23730
  this.visitNegativeLookahead(node);
23620
23731
  break;
23732
+ case "Lookbehind":
23733
+ this.visitLookbehind(node);
23734
+ break;
23735
+ case "NegativeLookbehind":
23736
+ this.visitNegativeLookbehind(node);
23737
+ break;
23621
23738
  case "Character":
23622
23739
  this.visitCharacter(node);
23623
23740
  break;
@@ -23657,6 +23774,10 @@ var init_base_regexp_visitor = __esm({
23657
23774
  }
23658
23775
  visitNegativeLookahead(node) {
23659
23776
  }
23777
+ visitLookbehind(node) {
23778
+ }
23779
+ visitNegativeLookbehind(node) {
23780
+ }
23660
23781
  // atoms
23661
23782
  visitCharacter(node) {
23662
23783
  }
@@ -23750,6 +23871,8 @@ function firstCharOptimizedIndices(ast, result, ignoreCase) {
23750
23871
  // assertions do not affect potential starting codes
23751
23872
  case "Lookahead":
23752
23873
  case "NegativeLookahead":
23874
+ case "Lookbehind":
23875
+ case "NegativeLookbehind":
23753
23876
  case "StartAnchor":
23754
23877
  case "WordBoundary":
23755
23878
  case "NonWordBoundary":
@@ -23894,6 +24017,12 @@ var init_reg_exp = __esm({
23894
24017
  case "NegativeLookahead":
23895
24018
  this.visitNegativeLookahead(node);
23896
24019
  return;
24020
+ case "Lookbehind":
24021
+ this.visitLookbehind(node);
24022
+ return;
24023
+ case "NegativeLookbehind":
24024
+ this.visitNegativeLookbehind(node);
24025
+ return;
23897
24026
  }
23898
24027
  super.visitChildren(node);
23899
24028
  }
@@ -24333,24 +24462,27 @@ function findUnreachablePatterns(tokenTypes) {
24333
24462
  }
24334
24463
  return result;
24335
24464
  }, []);
24336
- forEach_default(tokenTypes, (tokType, testIdx) => {
24337
- forEach_default(canBeTested, ({ str, idx, tokenType }) => {
24338
- if (testIdx < idx && testTokenType(str, tokType.PATTERN)) {
24339
- const msg = `Token: ->${tokenType.name}<- can never be matched.
24340
- Because it appears AFTER the Token Type ->${tokType.name}<-in the lexer's definition.
24465
+ forEach_default(tokenTypes, (aTokType, aIdx) => {
24466
+ forEach_default(canBeTested, ({ str: bStr, idx: bIdx, tokenType: bTokType }) => {
24467
+ if (aIdx < bIdx && tryToMatchStrToPattern(bStr, aTokType.PATTERN)) {
24468
+ const msg = `Token: ->${bTokType.name}<- can never be matched.
24469
+ Because it appears AFTER the Token Type ->${aTokType.name}<-in the lexer's definition.
24341
24470
  See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;
24342
24471
  errors.push({
24343
24472
  message: msg,
24344
24473
  type: LexerDefinitionErrorType.UNREACHABLE_PATTERN,
24345
- tokenTypes: [tokType, tokenType]
24474
+ tokenTypes: [aTokType, bTokType]
24346
24475
  });
24347
24476
  }
24348
24477
  });
24349
24478
  });
24350
24479
  return errors;
24351
24480
  }
24352
- function testTokenType(str, pattern) {
24481
+ function tryToMatchStrToPattern(str, pattern) {
24353
24482
  if (isRegExp_default(pattern)) {
24483
+ if (usesLookAheadOrBehind(pattern)) {
24484
+ return false;
24485
+ }
24354
24486
  const regExpArray = pattern.exec(str);
24355
24487
  return regExpArray !== null && regExpArray.index === 0;
24356
24488
  } else if (isFunction_default(pattern)) {
@@ -24381,6 +24513,9 @@ function noMetaChar(regExp) {
24381
24513
  ];
24382
24514
  return find_default(metaChars, (char) => regExp.source.indexOf(char) !== -1) === void 0;
24383
24515
  }
24516
+ function usesLookAheadOrBehind(regExp) {
24517
+ return /(\(\?=)|(\(\?!)|(\(\?<=)|(\(\?<!)/.test(regExp.source);
24518
+ }
24384
24519
  function addStartOfInput(pattern) {
24385
24520
  const flags = pattern.ignoreCase ? "i" : "";
24386
24521
  return new RegExp(`^(?:${pattern.source})`, flags);
@@ -24728,7 +24863,7 @@ var init_lexer_errors_public = __esm({
24728
24863
  buildUnableToPopLexerModeMessage(token) {
24729
24864
  return `Unable to pop Lexer Mode after encountering Token ->${token.image}<- The Mode Stack is empty`;
24730
24865
  },
24731
- buildUnexpectedCharactersMessage(fullText, startOffset, length, line, column) {
24866
+ buildUnexpectedCharactersMessage(fullText, startOffset, length, line, column, mode) {
24732
24867
  return `unexpected character: ->${fullText.charAt(startOffset)}<- at offset: ${startOffset}, skipped ${length} characters.`;
24733
24868
  }
24734
24869
  };
@@ -25169,7 +25304,7 @@ var init_lexer_public = __esm({
25169
25304
  }
25170
25305
  errLength = offset - errorStartOffset;
25171
25306
  column = this.computeNewColumn(column, errLength);
25172
- msg = this.config.errorMessageProvider.buildUnexpectedCharactersMessage(orgText, errorStartOffset, errLength, errorLine, errorColumn);
25307
+ msg = this.config.errorMessageProvider.buildUnexpectedCharactersMessage(orgText, errorStartOffset, errLength, errorLine, errorColumn, last_default(modeStack));
25173
25308
  errors.push({
25174
25309
  offset: errorStartOffset,
25175
25310
  line: errorLine,
@@ -25282,7 +25417,7 @@ var init_lexer_public = __esm({
25282
25417
  return regExpArray !== null ? regExpArray[0] : null;
25283
25418
  }
25284
25419
  };
25285
- Lexer.SKIPPED = "This marks a skipped Token pattern, this means each token identified by it willbe consumed and then thrown into oblivion, this can be used to for example to completely ignore whitespace.";
25420
+ Lexer.SKIPPED = "This marks a skipped Token pattern, this means each token identified by it will be consumed and then thrown into oblivion, this can be used to for example to completely ignore whitespace.";
25286
25421
  Lexer.NA = /NOT_APPLICABLE/;
25287
25422
  }
25288
25423
  });
@@ -61086,10 +61221,23 @@ ${toolResultContent}
61086
61221
  }
61087
61222
  } else if (this.toolImplementations[toolName]) {
61088
61223
  try {
61224
+ let resolvedWorkingDirectory = this.allowedFolders && this.allowedFolders[0] || process.cwd();
61225
+ if (params.workingDirectory) {
61226
+ const requestedDir = resolve4(params.workingDirectory);
61227
+ const isWithinAllowed = !this.allowedFolders || this.allowedFolders.length === 0 || this.allowedFolders.some((folder) => {
61228
+ const resolvedFolder = resolve4(folder);
61229
+ return requestedDir === resolvedFolder || requestedDir.startsWith(resolvedFolder + sep3);
61230
+ });
61231
+ if (isWithinAllowed) {
61232
+ resolvedWorkingDirectory = requestedDir;
61233
+ } else if (this.debug) {
61234
+ console.error(`[DEBUG] Rejected workingDirectory "${params.workingDirectory}" - not within allowed folders`);
61235
+ }
61236
+ }
61089
61237
  const toolParams = {
61090
61238
  ...params,
61091
61239
  sessionId: this.sessionId,
61092
- workingDirectory: this.allowedFolders && this.allowedFolders[0] || process.cwd()
61240
+ workingDirectory: resolvedWorkingDirectory
61093
61241
  };
61094
61242
  if (this.debug) {
61095
61243
  console.error(`