langchain-agentx-python 2.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl

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.
@@ -11,7 +11,7 @@ from langchain_agentx import create_loop_agent
11
11
  ```
12
12
  """
13
13
 
14
- __version__ = "2.0.0"
14
+ __version__ = "2.0.1"
15
15
 
16
16
  from .loop import ( # noqa: F401
17
17
  create_loop_agent,
@@ -30,6 +30,26 @@ from .ast_security import BashAstAnalysis, BashWrapperStripper
30
30
 
31
31
  OperationType = Literal["read", "write", "create"]
32
32
 
33
+ # CC pathValidation.ts GLOB_PATTERN_REGEX — read glob base-dir 校验 SSOT(非 glob.ts extractGlobBaseDirectory)
34
+ _GLOB_PATTERN_RE = re.compile(r"[*?\[\]{}]")
35
+
36
+
37
+ def get_glob_base_directory(path: str) -> str:
38
+ """从 glob pattern 提取 base 目录(对齐 CC pathValidation.ts getGlobBaseDirectory)。"""
39
+ match = _GLOB_PATTERN_RE.search(path)
40
+ if not match:
41
+ return path
42
+
43
+ before_glob = path[: match.start()]
44
+ if os.name == "nt":
45
+ last_sep = max(before_glob.rfind("/"), before_glob.rfind("\\"))
46
+ else:
47
+ last_sep = before_glob.rfind("/")
48
+ if last_sep == -1:
49
+ return "."
50
+ base = before_glob[:last_sep]
51
+ return base if base else "/"
52
+
33
53
 
34
54
  @dataclass(frozen=True)
35
55
  class BashPathTarget:
@@ -2164,6 +2184,23 @@ class BashPathSyntaxGuard:
2164
2184
  return True
2165
2185
  return False
2166
2186
 
2187
+ def has_non_glob_shell_expansion(self, raw_path: str) -> bool:
2188
+ """除 read 允许的未引号 glob 外,是否仍含需人工确认的 shell 展开语法。"""
2189
+ stripped = raw_path.strip()
2190
+ if not stripped:
2191
+ return False
2192
+
2193
+ scanner = BashPathLexicalScanner(stripped)
2194
+ if self._contains_parameter_like_expansion(scanner):
2195
+ return True
2196
+ if self._contains_unquoted_dynamic_tilde(scanner):
2197
+ return True
2198
+ if self._contains_unquoted_extglob(scanner):
2199
+ return True
2200
+ if self._contains_unquoted_brace_expansion(scanner):
2201
+ return True
2202
+ return False
2203
+
2167
2204
  def _contains_parameter_like_expansion(self, scanner: "BashPathLexicalScanner") -> bool:
2168
2205
  for index, char in enumerate(scanner.text):
2169
2206
  if scanner.is_single(index) or scanner.is_escaped(index):
@@ -2408,34 +2445,65 @@ class BashPathSecurityAnalyzer:
2408
2445
  ask_prompt=reason,
2409
2446
  )
2410
2447
 
2411
- syntax_reason = self._path_syntax_guard.get_manual_approval_reason(path_info)
2412
- if syntax_reason is not None:
2413
- semantic_kind = self._path_syntax_guard.get_syntax_semantic_kind(path_info)
2414
- metadata: dict[str, object] | None = None
2415
- if semantic_kind is not None:
2416
- metadata = {"path_syntax": {"semantic_kind": semantic_kind}}
2417
- return AuthorizationDecision(
2418
- behavior="ask",
2419
- message=syntax_reason,
2420
- policy_id="bash_path_validator",
2421
- ask_prompt=syntax_reason,
2422
- metadata=metadata,
2423
- )
2424
-
2425
2448
  dangerous_decision = self._dangerous_path_checker.check(path_info, cwd)
2426
2449
  if dangerous_decision is not None:
2427
2450
  return dangerous_decision
2428
2451
 
2429
2452
  authorize_path = getattr(policy, "authorize_path", None)
2430
- if authorize_path is None:
2431
- return AuthorizationDecision(behavior="allow")
2432
2453
 
2433
2454
  for target in path_info.path_targets:
2434
- resolved_path = self._resolve_path(target.raw_path, cwd)
2435
- decision = authorize_path(
2436
- resolved_path,
2437
- is_write=target.operation_type in {"write", "create"},
2438
- )
2455
+ raw_path = target.raw_path.strip()
2456
+ operation_type = target.operation_type
2457
+
2458
+ if self._path_syntax_guard.has_non_glob_shell_expansion(raw_path):
2459
+ if target.target_source == "redirection":
2460
+ reason = (
2461
+ "Shell expansion syntax in redirection targets requires explicit approval "
2462
+ "because the write path cannot be determined safely."
2463
+ )
2464
+ else:
2465
+ reason = (
2466
+ "Shell expansion syntax in filesystem path arguments requires explicit approval "
2467
+ "because the target path cannot be determined safely."
2468
+ )
2469
+ semantic_kind = self._path_syntax_guard._classify_target_semantic_kind(target)
2470
+ metadata: dict[str, object] | None = None
2471
+ if semantic_kind is not None:
2472
+ metadata = {"path_syntax": {"semantic_kind": semantic_kind}}
2473
+ return AuthorizationDecision(
2474
+ behavior="ask",
2475
+ message=reason,
2476
+ policy_id="bash_path_validator",
2477
+ ask_prompt=reason,
2478
+ metadata=metadata,
2479
+ )
2480
+
2481
+ if _GLOB_PATTERN_RE.search(raw_path) and operation_type in {"write", "create"}:
2482
+ reason = (
2483
+ "Glob patterns are not allowed in write operations. "
2484
+ "Please specify an exact file path."
2485
+ )
2486
+ return AuthorizationDecision(
2487
+ behavior="ask",
2488
+ message=reason,
2489
+ policy_id="bash_path_validator",
2490
+ ask_prompt=reason,
2491
+ metadata={"path_syntax": {"semantic_kind": "wildcard"}},
2492
+ )
2493
+
2494
+ if authorize_path is None:
2495
+ continue
2496
+
2497
+ if _GLOB_PATTERN_RE.search(raw_path) and operation_type == "read":
2498
+ base_path = get_glob_base_directory(raw_path)
2499
+ resolved_path = self._resolve_path(base_path, cwd)
2500
+ decision = authorize_path(resolved_path, is_write=False)
2501
+ else:
2502
+ resolved_path = self._resolve_path(raw_path, cwd)
2503
+ decision = authorize_path(
2504
+ resolved_path,
2505
+ is_write=operation_type in {"write", "create"},
2506
+ )
2439
2507
  if decision.behavior != "allow":
2440
2508
  return decision
2441
2509
 
@@ -25,13 +25,15 @@ class BashSemanticMatrixRow:
25
25
  semantic_kind: str | None
26
26
 
27
27
 
28
- # CC pathValidation / bashSecurity / bashPermissions 对齐子集(L1 ask + decision_reason)
28
+ # CC pathValidation / bashSecurity / bashPermissions 对齐子集(L1 behavior + decision_reason)
29
29
  BASH_WILDCARD_PREFIX_SEMANTIC_MATRIX: tuple[BashSemanticMatrixRow, ...] = (
30
- # --- wildcard / brace / glob(path_securitybash_path_validator)---
31
- BashSemanticMatrixRow("W01", "cat file*.txt", "ask", "bash_path_validator", "classifier", "wildcard"),
32
- BashSemanticMatrixRow("W02", "cat file?.txt", "ask", "bash_path_validator", "classifier", "wildcard"),
33
- BashSemanticMatrixRow("W03", "cat [ab].txt", "ask", "bash_path_validator", "classifier", "wildcard"),
30
+ # --- read glob(Phase 1:roots validateGlobPatternallow)---
31
+ BashSemanticMatrixRow("W01", "cat file*.txt", "allow", None, None, None),
32
+ BashSemanticMatrixRow("W02", "cat file?.txt", "allow", None, None, None),
33
+ BashSemanticMatrixRow("W03", "cat [ab].txt", "allow", None, None, None),
34
+ # --- brace / write glob(仍 ask;非 read glob base-dir 放行)---
34
35
  BashSemanticMatrixRow("W04", "cat src/{a,b}.txt", "ask", "bash_path_validator", "classifier", "wildcard"),
36
+ BashSemanticMatrixRow("W05", "touch foo*.txt", "ask", "bash_path_validator", "classifier", "wildcard"),
35
37
  # --- path expansion($ / ~+ / ${})---
36
38
  BashSemanticMatrixRow("E01", "cat $HOME/x", "ask", "bash_path_validator", "classifier", "path_expansion"),
37
39
  BashSemanticMatrixRow("E02", "cat ~+/file.txt", "ask", "bash_path_validator", "classifier", "path_expansion"),
@@ -64,6 +64,17 @@ def _is_descendant(child: str, parent: str) -> bool:
64
64
  return False
65
65
 
66
66
 
67
+ def _directory_listing_glob_hint(pattern: str) -> str | None:
68
+ """层 B:Glob 底层 rg --files 列文件不列目录名;pattern 含 ``*/`` 时提示 Agent。"""
69
+ if "*/" not in pattern:
70
+ return None
71
+ return (
72
+ "Glob lists files via ripgrep --files, not directory names. "
73
+ "Use pattern '**/*' with an explicit path to find files, "
74
+ "or Bash 'ls -d */' to list immediate subdirectory names."
75
+ )
76
+
77
+
67
78
  class GlobRuntimeTool(RuntimeTool):
68
79
  name: str = TOOL_NAME
69
80
  description: str = DESCRIPTION
@@ -308,11 +319,17 @@ class GlobRuntimeTool(RuntimeTool):
308
319
  f"No files in this page (offset {result.offset}, "
309
320
  f"{result.total_matches} total match(es))"
310
321
  )
322
+ hints: list[ToolHint] | None = None
323
+ dir_hint = _directory_listing_glob_hint(result.pattern)
324
+ if dir_hint is not None and result.total_matches == 0:
325
+ hints = [ToolHint(message=dir_hint)]
326
+ meta["semantic_note"] = "glob_lists_files_not_directories"
311
327
  return ToolResultEnvelope(
312
328
  status="ok",
313
329
  tool_name=self.name,
314
330
  summary=summary,
315
331
  payload=summary,
332
+ hints=hints,
316
333
  meta=meta,
317
334
  )
318
335
 
@@ -60,10 +60,14 @@ def run_ripgrep_lines(
60
60
  对齐 CC ripgrep.ts:returncode 1 -> [];EAGAIN 时以 -j 1 重试一次。
61
61
  供 RipgrepBackend 与 Glob 的 rg --files 列文件共用。
62
62
 
63
+ SDK 补充:``subprocess cwd=search_path``。CC 隐式依赖进程 cwd≈searchDir;
64
+ 编排布局 ``state_root≠repo`` 时须显式设 cwd,否则 Windows 上目录前缀 glob(如 ``alpha/*``)误空。
65
+
63
66
  若 ``cancel_event`` 非空,使用可中断子进程路径(``RgSubprocessController``),
64
67
  不再走 ``text_runner.run``(便于测试 mock 时仅覆盖无取消路径)。
65
68
  """
66
- cmd = [rg_path, *args, search_path]
69
+ search_cwd = os.path.realpath(search_path)
70
+ cmd = [rg_path, *args, search_cwd]
67
71
  if cancel_event is not None:
68
72
  if cancel_event.is_set():
69
73
  raise GrepCancelledError("ripgrep cancelled before start")
@@ -72,7 +76,10 @@ def run_ripgrep_lines(
72
76
  ctrl = RgSubprocessController()
73
77
  try:
74
78
  result = ctrl.run_argv_wait_completed(
75
- cmd, timeout=float(timeout), cancel_event=cancel_event
79
+ cmd,
80
+ timeout=float(timeout),
81
+ cancel_event=cancel_event,
82
+ cwd=search_cwd,
76
83
  )
77
84
  except GrepTimeoutError:
78
85
  raise
@@ -84,6 +91,7 @@ def run_ripgrep_lines(
84
91
  cmd,
85
92
  capture_output=True,
86
93
  timeout=timeout,
94
+ cwd=search_cwd,
87
95
  )
88
96
  except subprocess.TimeoutExpired:
89
97
  raise GrepTimeoutError(f"ripgrep search timed out after {timeout}s")
@@ -312,7 +320,8 @@ class RipgrepBackend:
312
320
  multiline=multiline,
313
321
  plugin_glob_exclusions=plugin_glob_exclusions,
314
322
  )
315
- cmd = [self._rg_path, *args, search_path]
323
+ cmd = [self._rg_path, *args, os.path.realpath(search_path)]
324
+ search_cwd = os.path.realpath(search_path)
316
325
  if cancel_event is not None and cancel_event.is_set():
317
326
  raise GrepCancelledError("ripgrep cancelled before start")
318
327
  try:
@@ -321,6 +330,7 @@ class RipgrepBackend:
321
330
  stdout=subprocess.PIPE,
322
331
  stderr=subprocess.PIPE,
323
332
  text=True,
333
+ cwd=search_cwd,
324
334
  )
325
335
  except FileNotFoundError as e:
326
336
  raise GrepExecutionError(
@@ -37,6 +37,7 @@ class RgSubprocessController:
37
37
  *,
38
38
  timeout: float,
39
39
  cancel_event: threading.Event | None,
40
+ cwd: str | None = None,
40
41
  ) -> subprocess.CompletedProcess[str]:
41
42
  """
42
43
  启动 ``argv``,在 ``timeout`` 秒内等待结束;若 ``cancel_event`` 被 set 则 terminate/kill。
@@ -55,6 +56,7 @@ class RgSubprocessController:
55
56
  text=True,
56
57
  encoding="utf-8",
57
58
  errors="replace",
59
+ cwd=cwd,
58
60
  )
59
61
  except FileNotFoundError as e:
60
62
  raise GrepExecutionError(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langchain-agentx-python
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: LangChain/LangGraph-based agent utilities for CodeBaseX.
5
5
  Author-email: GoodMood2008 <GoodMood2008@users.noreply.github.com>
6
6
  License: Apache License
@@ -1,4 +1,4 @@
1
- langchain_agentx/__init__.py,sha256=NbsbOU5jFUfBXQDL1bLLkmKXGdLM9-eOdk0p7cNSPIY,1678
1
+ langchain_agentx/__init__.py,sha256=mIitE_209ZGQxVtUqOUCj_SxLxyx1fxbhZz6VZhlo_c,1678
2
2
  langchain_agentx/command/__init__.py,sha256=Ej260S5uFQcmEtGZQG_NH7BYjHoStrpBLtGUsBTxyZk,631
3
3
  langchain_agentx/command/allowed_tools.py,sha256=TVA0VM8rm98H-QbLK_GRiX5RhEAZFxKawliMi4kk96A,3359
4
4
  langchain_agentx/command/context.py,sha256=DIGOGPBw5UGDBm0WNNJlKx1h_9rCRmcdROv4DT61Qug,731
@@ -535,7 +535,7 @@ langchain_agentx/tools/bash/models.py,sha256=Zq0O5_uXITa-iDeG-8-KtQwDq0dWxkpQq4q
535
535
  langchain_agentx/tools/bash/observability.py,sha256=tam0WCLxQ7A-dxS9VBHjBfh2t6njRekUFEV5wwN4Eo0,4267
536
536
  langchain_agentx/tools/bash/output_tracker.py,sha256=JJv_PVKEoFJljUrjlIjzlRx6GVoT19fYIOsgQnlwBgs,3373
537
537
  langchain_agentx/tools/bash/output_utils.py,sha256=WYU2vVOjo9HQKSeQ_z88KxJsrvlFm0wf7USCMvuifZ8,6638
538
- langchain_agentx/tools/bash/path_security.py,sha256=L7zFXoyBDYLtd2bVLIeMg-xuCkSD7-Tt2EKlvKR_ssI,94984
538
+ langchain_agentx/tools/bash/path_security.py,sha256=6mKjXkCyQhPzsMoTITr5KhTxYE7me5bM7hhz2wTikxs,97828
539
539
  langchain_agentx/tools/bash/prompt.py,sha256=28WBRLt5G6e-OUildOFbeEyw7eL3iI8T40pEwmWVYxo,12527
540
540
  langchain_agentx/tools/bash/read_only_validation.py,sha256=rtNW6nK7sQMzR6LGc420kUd5wE30DnYX8jjuW7hUFWA,24822
541
541
  langchain_agentx/tools/bash/result_presenter.py,sha256=OkJid40AmrAsRxhY7jSaw_PgRMoIfMcj6BNXaqcryTo,14946
@@ -543,7 +543,7 @@ langchain_agentx/tools/bash/sandbox_decision.py,sha256=T_JaTfPADDcH2mRtrpKTBbIVo
543
543
  langchain_agentx/tools/bash/security.py,sha256=YM34IUvNMDskoEA-WTG7hiuziY90DAFqQngpntGOtEc,7807
544
544
  langchain_agentx/tools/bash/sed_edit_parser.py,sha256=tTDLMj5f5C4nkcf1Ykn9uMKJtEQNm3ejrKQOal2sI9s,7855
545
545
  langchain_agentx/tools/bash/sed_validation.py,sha256=HA-zWe8Wo4LbgC9x8ZMQLzkd73i4IFeay0GhNb2qM20,5704
546
- langchain_agentx/tools/bash/semantic_matrix.py,sha256=rWO31jB2TxOSmCAMYKe3bkQ6YOHU7pCMX0--OABP5kM,3184
546
+ langchain_agentx/tools/bash/semantic_matrix.py,sha256=stvsudu8mGjS1JJCawMAUKabt-7co90Q8qZgi063QSw,3285
547
547
  langchain_agentx/tools/bash/semantics.py,sha256=pkf4URTulAOVL7m04Rq9JJF7z0J2mTEsGWrjyoY9r6o,5769
548
548
  langchain_agentx/tools/bash/shell_locator.py,sha256=AUnCZS8gmZurCs8rL4y0MMOS9UZimfg5rQ5At38mZKU,6296
549
549
  langchain_agentx/tools/bash/shell_quoting.py,sha256=K4JLVGwU8VpdQzfQB1o3xsq4HoCKHew1pry8hHChcoA,4498
@@ -570,12 +570,12 @@ langchain_agentx/tools/glob/pagination.py,sha256=qn3HjCpvcARgXXRqhsQQ5huibPIu9Kc
570
570
  langchain_agentx/tools/glob/prompt.py,sha256=oPEXucn9P2z01ghXEtDpGGKR3KpOp6wH8PDDYaoiN6U,686
571
571
  langchain_agentx/tools/glob/rg_list_backend.py,sha256=gTyFQE0eYHl-7LkSdah76S8bQnLCgi-6ka6jixeSI4s,5006
572
572
  langchain_agentx/tools/glob/rg_pattern.py,sha256=YOV-1gdxC52vzdAwgz27l7mzPJvVuHqpqeWhiEnWQJs,1377
573
- langchain_agentx/tools/glob/tool.py,sha256=WZ3GjKEYtsI9l2U60b38Hnisu9pSwnZjN9xReH-fzUM,13664
573
+ langchain_agentx/tools/glob/tool.py,sha256=fNOUrt-YUnXXOODiHVAtS0xz9qxidFPuJ9DGRh-R1cY,14445
574
574
  langchain_agentx/tools/grep/__init__.py,sha256=AA9qa42Ap9tw6lPQkNgKzYmr4hu44eP93qVdyP-eFNU,162
575
- langchain_agentx/tools/grep/backend.py,sha256=2PcavYBJdI1fD1sEE93geWhycD7C-PZL-HI6oVyGPb4,13422
575
+ langchain_agentx/tools/grep/backend.py,sha256=DSKetuS6Wcta5wrDBVFYSVDLsou0VDXXZyvdFvN1nls,13885
576
576
  langchain_agentx/tools/grep/models.py,sha256=6fTSux0COK06weQ7PWYqCGBQx60OaNtqC5q7xKtf5i0,3860
577
577
  langchain_agentx/tools/grep/prompt.py,sha256=Euo1NErphk5WHxaKXwJH-1v7joblfwEFWHIYNthKkzo,1336
578
- langchain_agentx/tools/grep/rg_subprocess_controller.py,sha256=EsDu13sCUu18x7H38rpJiQ6qNWrSXauqA55vCttSVEE,3999
578
+ langchain_agentx/tools/grep/rg_subprocess_controller.py,sha256=ijMJnKPo7iEDFBz-_YipTiPusrL6OO2XbWxHUuwpMfA,4056
579
579
  langchain_agentx/tools/grep/tool.py,sha256=ngfLGdCrku_XfV_YfpoI3TyXiEqHcJ8wxcLz81mDMok,19795
580
580
  langchain_agentx/tools/plan_mode/__init__.py,sha256=wO5FX24xEB0Dx9h6bysBt1ql3wn0rjPYsg70mbl4S_0,402
581
581
  langchain_agentx/tools/plan_mode/constants.py,sha256=vBjcx9JTSOhU9jfCJ4RKnCxVQPn0HzHapouKxRf2Zns,293
@@ -751,8 +751,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
751
751
  langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
752
752
  langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
753
753
  langchain_agentx/workspace/view.py,sha256=PGasqTaqhlD03SXXazHuw4RHfV681AIdlsYqL70pEjc,4774
754
- langchain_agentx_python-2.0.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
755
- langchain_agentx_python-2.0.0.dist-info/METADATA,sha256=_pvjskTD_-jwlxU8f4XlXCarN8PP3usISTTOa9C8iec,24844
756
- langchain_agentx_python-2.0.0.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
757
- langchain_agentx_python-2.0.0.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
758
- langchain_agentx_python-2.0.0.dist-info/RECORD,,
754
+ langchain_agentx_python-2.0.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
755
+ langchain_agentx_python-2.0.1.dist-info/METADATA,sha256=Md4WdCIDe34uD_fT4C8MrwhHW_yXKLNfIsgKvFU9Bv0,24844
756
+ langchain_agentx_python-2.0.1.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
757
+ langchain_agentx_python-2.0.1.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
758
+ langchain_agentx_python-2.0.1.dist-info/RECORD,,