langchain-agentx-python 2.2.9__py3-none-any.whl → 2.3.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.
- langchain_agentx/__init__.py +1 -1
- langchain_agentx/loop/graph/factory.py +11 -2
- langchain_agentx/tool_runtime/inner_permission_chain.py +7 -6
- langchain_agentx/tool_runtime/loader.py +1 -0
- langchain_agentx/tool_runtime/loop_loader.py +12 -1
- langchain_agentx/tool_runtime/permission_rules.py +9 -0
- langchain_agentx/tool_runtime/policy.py +23 -3
- langchain_agentx/tool_runtime/process_loader.py +24 -7
- langchain_agentx/tools/bash/bash_registry_content.py +106 -0
- langchain_agentx/tools/bash/command_allow_rules.py +65 -19
- langchain_agentx/tools/bash/shell_rule_matching.py +143 -0
- langchain_agentx/tools/bash/tool.py +11 -1
- langchain_agentx/tools/skill/registry_permission.py +99 -0
- langchain_agentx/tools/skill/source_paths.py +21 -0
- langchain_agentx/tools/skill/tool.py +18 -0
- langchain_agentx/tools/webfetch/registry_permission.py +37 -2
- {langchain_agentx_python-2.2.9.dist-info → langchain_agentx_python-2.3.1.dist-info}/METADATA +1 -1
- {langchain_agentx_python-2.2.9.dist-info → langchain_agentx_python-2.3.1.dist-info}/RECORD +21 -18
- {langchain_agentx_python-2.2.9.dist-info → langchain_agentx_python-2.3.1.dist-info}/LICENSE +0 -0
- {langchain_agentx_python-2.2.9.dist-info → langchain_agentx_python-2.3.1.dist-info}/WHEEL +0 -0
- {langchain_agentx_python-2.2.9.dist-info → langchain_agentx_python-2.3.1.dist-info}/top_level.txt +0 -0
langchain_agentx/__init__.py
CHANGED
|
@@ -756,8 +756,17 @@ class LoopGraphBuilder:
|
|
|
756
756
|
include_agent=not user_has_agent_tool,
|
|
757
757
|
)
|
|
758
758
|
for tool in self._services.loader.registry.list():
|
|
759
|
-
if tool.name
|
|
760
|
-
self._loop_loader.
|
|
759
|
+
if tool.name in self._loop_loader.registry._tools:
|
|
760
|
+
existing = self._loop_loader.registry.get(tool.name)
|
|
761
|
+
if existing is not tool:
|
|
762
|
+
logger.warning(
|
|
763
|
+
"Host loader tool %r not merged into loop; "
|
|
764
|
+
"register_default_tools already registered %r (S-4)",
|
|
765
|
+
tool.name,
|
|
766
|
+
tool.name,
|
|
767
|
+
)
|
|
768
|
+
continue
|
|
769
|
+
self._loop_loader.register(tool)
|
|
761
770
|
self._runtime_tools = self._loop_loader.registry.list()
|
|
762
771
|
from ...loop.prompt.tool_aware_sections import collect_enabled_tool_names
|
|
763
772
|
|
|
@@ -209,12 +209,13 @@ class InnerPermissionChain:
|
|
|
209
209
|
command = data.get("command")
|
|
210
210
|
if not isinstance(command, str) or not command.strip():
|
|
211
211
|
return None
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
212
|
+
from langchain_agentx.tools.bash.bash_registry_content import (
|
|
213
|
+
evaluate_bash_inner_content_rules,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
content_decision = evaluate_bash_inner_content_rules(command, registry)
|
|
217
|
+
if content_decision is not None:
|
|
218
|
+
return content_decision
|
|
218
219
|
return None
|
|
219
220
|
|
|
220
221
|
path = self._engine._extract_path(data)
|
|
@@ -155,6 +155,7 @@ class ToolRuntimeLoader:
|
|
|
155
155
|
目的:为 subagent 组装“过滤后的工具池”而不丢失权限/管控语义。
|
|
156
156
|
"""
|
|
157
157
|
child = ToolRuntimeLoader(
|
|
158
|
+
policy_config=self._process.policy_config,
|
|
158
159
|
policy_engine=self._process.policy,
|
|
159
160
|
headless=self._process.headless if headless is None else headless,
|
|
160
161
|
prompt_handler=self._process.prompt_handler,
|
|
@@ -107,6 +107,7 @@ class LoopLoader:
|
|
|
107
107
|
agent_home_segment=self._agent_home,
|
|
108
108
|
session_cwd=effective_cwd,
|
|
109
109
|
)
|
|
110
|
+
self._merge_skill_read_roots_policy(cfg)
|
|
110
111
|
self._inject_project_memory_policy(cfg)
|
|
111
112
|
plugin_ex = PluginCacheGlobExclusions(
|
|
112
113
|
cache_root=str(cfg.plugin_cache_dir.resolve()),
|
|
@@ -210,11 +211,21 @@ class LoopLoader:
|
|
|
210
211
|
setter(patterns)
|
|
211
212
|
return patterns
|
|
212
213
|
|
|
214
|
+
def _merge_skill_read_roots_policy(self, workspace_cfg: Any) -> None:
|
|
215
|
+
"""G-2:skill 发现目录并入 read_roots(在 project_memory 注入之前)。"""
|
|
216
|
+
from langchain_agentx.tools.skill.source_paths import resolve_skill_read_roots
|
|
217
|
+
|
|
218
|
+
roots = resolve_skill_read_roots(workspace_cfg)
|
|
219
|
+
if not roots:
|
|
220
|
+
return
|
|
221
|
+
base = self._process.effective_policy_config()
|
|
222
|
+
self._process.apply_policy_config(base.with_merged_read_roots(roots))
|
|
223
|
+
|
|
213
224
|
def _inject_project_memory_policy(self, workspace_cfg: Any) -> None:
|
|
214
225
|
"""Per-loop 注入 CC projects 树读写根与 path_safety layout。"""
|
|
215
226
|
layout = workspace_cfg.project_memory_layout
|
|
216
227
|
workspace_root = str(Path(self._workspace_root).resolve())
|
|
217
|
-
base = self._process.
|
|
228
|
+
base = self._process.effective_policy_config()
|
|
218
229
|
if not base.read_roots:
|
|
219
230
|
base = replace(base, read_roots=[workspace_root])
|
|
220
231
|
if not base.write_roots:
|
|
@@ -127,6 +127,15 @@ def _content_rule_matches_target(
|
|
|
127
127
|
target=target,
|
|
128
128
|
rule_content=rule_content,
|
|
129
129
|
)
|
|
130
|
+
if tool_name == "Skill":
|
|
131
|
+
from langchain_agentx.tools.skill.registry_permission import (
|
|
132
|
+
skill_rule_content_matches,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return skill_rule_content_matches(
|
|
136
|
+
skill_name=target,
|
|
137
|
+
rule_content=rule_content,
|
|
138
|
+
)
|
|
130
139
|
return fnmatch.fnmatch(target, rule_content)
|
|
131
140
|
|
|
132
141
|
|
|
@@ -45,9 +45,11 @@ from __future__ import annotations
|
|
|
45
45
|
import fnmatch
|
|
46
46
|
import os
|
|
47
47
|
import sys
|
|
48
|
+
from pathlib import Path
|
|
48
49
|
import warnings
|
|
49
50
|
from abc import ABC, abstractmethod
|
|
50
51
|
from dataclasses import dataclass, field, replace
|
|
52
|
+
from collections.abc import Iterable
|
|
51
53
|
from typing import TYPE_CHECKING, Any
|
|
52
54
|
|
|
53
55
|
from .models import AuthorizationDecision, ToolExecutionContext
|
|
@@ -123,6 +125,20 @@ class ToolPolicyConfig:
|
|
|
123
125
|
normalize_tool_name_set(self.cli_allow_tools),
|
|
124
126
|
)
|
|
125
127
|
|
|
128
|
+
def with_merged_read_roots(
|
|
129
|
+
self,
|
|
130
|
+
extra: Iterable[str | os.PathLike[str]],
|
|
131
|
+
) -> "ToolPolicyConfig":
|
|
132
|
+
"""Union ``read_roots``;保留 cliArg / rules 等字段(Phase 2 skill 发现根)。"""
|
|
133
|
+
merged: list[str] = []
|
|
134
|
+
seen: set[str] = set()
|
|
135
|
+
for raw in (*self.read_roots, *(str(Path(p).expanduser().resolve()) for p in extra)):
|
|
136
|
+
if not raw or raw in seen:
|
|
137
|
+
continue
|
|
138
|
+
seen.add(raw)
|
|
139
|
+
merged.append(raw)
|
|
140
|
+
return replace(self, read_roots=merged)
|
|
141
|
+
|
|
126
142
|
def with_project_memory_layout(
|
|
127
143
|
self,
|
|
128
144
|
layout: "ProjectMemoryLayout",
|
|
@@ -313,11 +329,16 @@ class DefaultPolicyEngine(PolicyEngine):
|
|
|
313
329
|
config: 策略配置
|
|
314
330
|
auto_mode_hook: Auto Mode 授权 Hook(可选,Phase 1 集成)
|
|
315
331
|
"""
|
|
332
|
+
self._auto_mode_hook = auto_mode_hook
|
|
333
|
+
self._internal_paths = InternalPathEvaluator()
|
|
334
|
+
self._agent_home_bypass = AgentHomeSessionBypassEvaluator()
|
|
335
|
+
self.replace_policy_config(config)
|
|
336
|
+
|
|
337
|
+
def replace_policy_config(self, config: ToolPolicyConfig) -> None:
|
|
338
|
+
"""原地更新 config 与派生 roots/authorizer(loop project memory 注入)。"""
|
|
316
339
|
self._config = config
|
|
317
340
|
self._read_roots = [os.path.realpath(r) for r in config.read_roots]
|
|
318
341
|
self._write_roots = [os.path.realpath(r) for r in config.write_roots]
|
|
319
|
-
self._auto_mode_hook = auto_mode_hook
|
|
320
|
-
self._internal_paths = InternalPathEvaluator()
|
|
321
342
|
self._path_safety: PathSafetyEvaluator | None = None
|
|
322
343
|
if config.enable_path_safety and config.path_safety is not None:
|
|
323
344
|
self._path_safety = PathSafetyEvaluator(
|
|
@@ -341,7 +362,6 @@ class DefaultPolicyEngine(PolicyEngine):
|
|
|
341
362
|
self._within_root(real_path, r) for r in self._read_roots
|
|
342
363
|
),
|
|
343
364
|
)
|
|
344
|
-
self._agent_home_bypass = AgentHomeSessionBypassEvaluator()
|
|
345
365
|
|
|
346
366
|
def authorize(
|
|
347
367
|
self,
|
|
@@ -50,14 +50,21 @@ class ProcessLoader:
|
|
|
50
50
|
permission_resolver=permission_resolver,
|
|
51
51
|
)
|
|
52
52
|
self._adapter = LangChainAdapter()
|
|
53
|
-
self._policy_config = policy_config
|
|
54
53
|
if policy_engine is not None:
|
|
55
54
|
self._policy: PolicyEngine | None = policy_engine
|
|
55
|
+
if policy_config is not None:
|
|
56
|
+
self._policy_config = policy_config
|
|
57
|
+
elif isinstance(policy_engine, DefaultPolicyEngine):
|
|
58
|
+
self._policy_config = policy_engine._config
|
|
59
|
+
else:
|
|
60
|
+
self._policy_config = None
|
|
56
61
|
elif policy_config is not None:
|
|
62
|
+
self._policy_config = policy_config
|
|
57
63
|
self._policy = DefaultPolicyEngine(policy_config)
|
|
58
64
|
else:
|
|
59
|
-
|
|
60
|
-
self._policy_config =
|
|
65
|
+
empty = ToolPolicyConfig()
|
|
66
|
+
self._policy_config = empty
|
|
67
|
+
self._policy = DefaultPolicyEngine(empty)
|
|
61
68
|
|
|
62
69
|
def set_permission_resolver(
|
|
63
70
|
self,
|
|
@@ -141,12 +148,22 @@ class ProcessLoader:
|
|
|
141
148
|
def permission_resolver(self) -> PermissionResolver | None:
|
|
142
149
|
return self._permission_resolver
|
|
143
150
|
|
|
151
|
+
def effective_policy_config(self) -> ToolPolicyConfig:
|
|
152
|
+
"""policy_config → DefaultPolicyEngine._config → 空配置(loop 注入 SSOT)。"""
|
|
153
|
+
if self._policy_config is not None:
|
|
154
|
+
return self._policy_config
|
|
155
|
+
policy = self._policy
|
|
156
|
+
if isinstance(policy, DefaultPolicyEngine):
|
|
157
|
+
return policy._config
|
|
158
|
+
return ToolPolicyConfig()
|
|
159
|
+
|
|
144
160
|
def apply_policy_config(self, policy_config: ToolPolicyConfig) -> None:
|
|
145
|
-
"""
|
|
146
|
-
auto_hook = None
|
|
147
|
-
if isinstance(self._policy, DefaultPolicyEngine):
|
|
148
|
-
auto_hook = self._policy._auto_mode_hook
|
|
161
|
+
"""替换策略配置;DefaultPolicyEngine 原地 rebind,保留 engine 实例 id。"""
|
|
149
162
|
self._policy_config = policy_config
|
|
163
|
+
if isinstance(self._policy, DefaultPolicyEngine):
|
|
164
|
+
self._policy.replace_policy_config(policy_config)
|
|
165
|
+
return
|
|
166
|
+
auto_hook = None
|
|
150
167
|
self._policy = DefaultPolicyEngine(policy_config, auto_mode_hook=auto_hook)
|
|
151
168
|
|
|
152
169
|
def sync_permission_rules_from_disk(
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/bash/bash_registry_content.py — Bash cliArg 内容 deny/ask(compound 段级 · Phase D)
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
Inner 段 1 对 Bash 按 AST permission_segments 评估内容级 deny/ask(对齐 CC compound 子命令优先 deny)。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
InnerPermissionChain._segment_1_2_global(Bash 分支)。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from langchain_agentx.tool_runtime.models import AuthorizationDecision
|
|
14
|
+
from langchain_agentx.tool_runtime.permission_rule_matcher import (
|
|
15
|
+
RegistryPermissionMatcher,
|
|
16
|
+
_decision_from_legacy_glob,
|
|
17
|
+
_decision_from_rule,
|
|
18
|
+
)
|
|
19
|
+
from langchain_agentx.tool_runtime.permission_rules import (
|
|
20
|
+
PermissionRuleBehavior,
|
|
21
|
+
PermissionRuleRegistry,
|
|
22
|
+
)
|
|
23
|
+
from langchain_agentx.tools.bash.shell_rule_matching import match_shell_permission_rule
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def bash_permission_segments(command: str) -> list[str]:
|
|
27
|
+
from langchain_agentx.tools.bash.ast_security import BashAstAnalyzer
|
|
28
|
+
|
|
29
|
+
analysis = BashAstAnalyzer().analyze(command)
|
|
30
|
+
segments = [s.strip() for s in analysis.permission_segments if s and s.strip()]
|
|
31
|
+
return segments or [command.strip()]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _segment_content_decision(
|
|
35
|
+
segment: str,
|
|
36
|
+
registry: PermissionRuleRegistry,
|
|
37
|
+
behavior: PermissionRuleBehavior,
|
|
38
|
+
) -> AuthorizationDecision | None:
|
|
39
|
+
matcher = RegistryPermissionMatcher(registry)
|
|
40
|
+
if behavior is PermissionRuleBehavior.DENY:
|
|
41
|
+
direct = matcher.match_deny("Bash", segment)
|
|
42
|
+
else:
|
|
43
|
+
direct = matcher.match_ask("Bash", segment)
|
|
44
|
+
if direct is not None:
|
|
45
|
+
return direct
|
|
46
|
+
|
|
47
|
+
legacy = registry.find_legacy_glob_match(segment, behavior)
|
|
48
|
+
if legacy is not None:
|
|
49
|
+
return _decision_from_legacy_glob(legacy, behavior=behavior.value)
|
|
50
|
+
|
|
51
|
+
for rule in registry.iter_rules(behavior):
|
|
52
|
+
if rule.tool_name != "Bash" or not rule.rule_content:
|
|
53
|
+
continue
|
|
54
|
+
if match_shell_permission_rule(rule.rule_content, segment, match_mode="prefix"):
|
|
55
|
+
return _decision_from_rule(rule, behavior=behavior.value)
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def evaluate_bash_inner_content_rules(
|
|
60
|
+
command: str,
|
|
61
|
+
registry: PermissionRuleRegistry,
|
|
62
|
+
) -> AuthorizationDecision | None:
|
|
63
|
+
"""CC compound:段级 deny 优先 → full deny/ask → 段级 ask。"""
|
|
64
|
+
cmd = command.strip()
|
|
65
|
+
if not cmd:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
matcher = RegistryPermissionMatcher(registry)
|
|
69
|
+
full_deny = matcher.match_deny("Bash", cmd)
|
|
70
|
+
if full_deny is not None:
|
|
71
|
+
return full_deny
|
|
72
|
+
|
|
73
|
+
segments = bash_permission_segments(cmd)
|
|
74
|
+
if len(segments) > 1:
|
|
75
|
+
for segment in segments:
|
|
76
|
+
seg_deny = _segment_content_decision(
|
|
77
|
+
segment, registry, PermissionRuleBehavior.DENY
|
|
78
|
+
)
|
|
79
|
+
if seg_deny is not None:
|
|
80
|
+
return seg_deny
|
|
81
|
+
|
|
82
|
+
full_ask = matcher.match_ask("Bash", cmd)
|
|
83
|
+
if full_ask is not None:
|
|
84
|
+
return full_ask
|
|
85
|
+
|
|
86
|
+
if len(segments) > 1:
|
|
87
|
+
first_ask: AuthorizationDecision | None = None
|
|
88
|
+
for segment in segments:
|
|
89
|
+
seg_ask = _segment_content_decision(
|
|
90
|
+
segment, registry, PermissionRuleBehavior.ASK
|
|
91
|
+
)
|
|
92
|
+
if seg_ask is not None and first_ask is None:
|
|
93
|
+
first_ask = seg_ask
|
|
94
|
+
if first_ask is not None:
|
|
95
|
+
return first_ask
|
|
96
|
+
|
|
97
|
+
if len(segments) == 1:
|
|
98
|
+
return _segment_content_decision(
|
|
99
|
+
segments[0], registry, PermissionRuleBehavior.DENY
|
|
100
|
+
) or _segment_content_decision(
|
|
101
|
+
segments[0], registry, PermissionRuleBehavior.ASK
|
|
102
|
+
)
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
__all__ = ["bash_permission_segments", "evaluate_bash_inner_content_rules"]
|
|
@@ -10,9 +10,10 @@ tools/bash/command_allow_rules.py — Bash 内容级 allow 匹配(slash comman
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
-
import fnmatch
|
|
14
13
|
from typing import TYPE_CHECKING
|
|
15
14
|
|
|
15
|
+
from langchain_agentx.tools.bash.shell_rule_matching import match_shell_permission_rule
|
|
16
|
+
|
|
16
17
|
from langchain_agentx.tool_runtime.models import AuthorizationDecision, ToolExecutionContext
|
|
17
18
|
from langchain_agentx.tool_runtime.permission_context import get_command_bash_allow_rules
|
|
18
19
|
from langchain_agentx.tool_runtime.permission_rule_matcher import RegistryPermissionMatcher
|
|
@@ -23,27 +24,24 @@ from langchain_agentx.tool_runtime.permission_rules import (
|
|
|
23
24
|
)
|
|
24
25
|
from langchain_agentx.tools.bash.read_only_validation import BashReadOnlyClassifier
|
|
25
26
|
|
|
27
|
+
from langchain_agentx.tool_runtime.policy import PolicyEngine, resolve_default_policy_engine
|
|
28
|
+
|
|
26
29
|
if TYPE_CHECKING:
|
|
27
30
|
from langchain_agentx.tool_runtime.policy import DefaultPolicyEngine
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
def bash_command_matches_allow_rule(command: str, rule_content: str) -> bool:
|
|
31
|
-
"""CC prefix
|
|
34
|
+
"""CC shellRuleMatching(prefix / wildcard / exact)。"""
|
|
32
35
|
cmd = command.strip()
|
|
33
36
|
pattern = rule_content.strip()
|
|
34
37
|
if not cmd or not pattern:
|
|
35
38
|
return False
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if "*" in pattern or "?" in pattern or "[" in pattern:
|
|
44
|
-
return fnmatch.fnmatch(cmd, pattern)
|
|
45
|
-
|
|
46
|
-
return cmd == pattern or cmd.startswith(f"{pattern} ")
|
|
39
|
+
return match_shell_permission_rule(
|
|
40
|
+
pattern,
|
|
41
|
+
cmd,
|
|
42
|
+
match_mode="prefix",
|
|
43
|
+
is_compound=False,
|
|
44
|
+
)
|
|
47
45
|
|
|
48
46
|
|
|
49
47
|
def try_command_allow_decision(
|
|
@@ -84,13 +82,14 @@ def cliarg_bash_has_content_allow_without_bare_tool(
|
|
|
84
82
|
def try_registry_bash_content_allow_decision(
|
|
85
83
|
command: str,
|
|
86
84
|
ctx: ToolExecutionContext,
|
|
87
|
-
policy_engine:
|
|
85
|
+
policy_engine: PolicyEngine | None,
|
|
88
86
|
) -> AuthorizationDecision | None:
|
|
89
87
|
"""cliArg/settings 等内容级 Bash allow(CC bashPermissions prefix allow,path 通过后)。"""
|
|
90
|
-
|
|
88
|
+
engine = resolve_default_policy_engine(policy_engine)
|
|
89
|
+
if engine is None:
|
|
91
90
|
return None
|
|
92
91
|
registry = build_permission_rule_registry(
|
|
93
|
-
policy_config=
|
|
92
|
+
policy_config=engine._config,
|
|
94
93
|
session_store=ctx.session_store,
|
|
95
94
|
workspace_root=ctx.workspace_root,
|
|
96
95
|
agent_home_segment=ctx.agent_home_segment,
|
|
@@ -99,18 +98,65 @@ def try_registry_bash_content_allow_decision(
|
|
|
99
98
|
return matcher.match_allow("Bash", command.strip())
|
|
100
99
|
|
|
101
100
|
|
|
101
|
+
def registry_has_cliarg_bash_allow(registry: PermissionRuleRegistry) -> bool:
|
|
102
|
+
for rule in registry.iter_rules(PermissionRuleBehavior.ALLOW):
|
|
103
|
+
if rule.source_name == "cliArg" and rule.tool_name == "Bash":
|
|
104
|
+
return True
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def promote_unscoped_bash_passthrough_to_ask(
|
|
109
|
+
decision: AuthorizationDecision,
|
|
110
|
+
*,
|
|
111
|
+
command: str,
|
|
112
|
+
ctx: ToolExecutionContext,
|
|
113
|
+
policy_engine: PolicyEngine | None,
|
|
114
|
+
) -> AuthorizationDecision:
|
|
115
|
+
"""无 cliArg Bash allow 时,write_roots 静默 allow → ask(对齐 CC passthrough)。"""
|
|
116
|
+
engine = resolve_default_policy_engine(policy_engine)
|
|
117
|
+
if engine is None or decision.behavior != "allow":
|
|
118
|
+
return decision
|
|
119
|
+
if _READ_ONLY_CLASSIFIER.classify(
|
|
120
|
+
command.strip(),
|
|
121
|
+
shell_ops_already_resolved=True,
|
|
122
|
+
).is_read_only:
|
|
123
|
+
return decision
|
|
124
|
+
if decision.policy_id != "write_roots":
|
|
125
|
+
return decision
|
|
126
|
+
registry = build_permission_rule_registry(
|
|
127
|
+
policy_config=engine._config,
|
|
128
|
+
session_store=ctx.session_store,
|
|
129
|
+
workspace_root=ctx.workspace_root,
|
|
130
|
+
agent_home_segment=ctx.agent_home_segment,
|
|
131
|
+
)
|
|
132
|
+
if registry_has_cliarg_bash_allow(registry):
|
|
133
|
+
return decision
|
|
134
|
+
if cliarg_bash_has_content_allow_without_bare_tool(registry):
|
|
135
|
+
return decision
|
|
136
|
+
return AuthorizationDecision(
|
|
137
|
+
behavior="ask",
|
|
138
|
+
message=decision.message,
|
|
139
|
+
policy_id="bash_passthrough",
|
|
140
|
+
ask_prompt=(
|
|
141
|
+
decision.ask_prompt
|
|
142
|
+
or f"Allow Bash command?\n{command.strip()}"
|
|
143
|
+
),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
102
147
|
def promote_unmatched_cliarg_content_only_allow_to_ask(
|
|
103
148
|
decision: AuthorizationDecision,
|
|
104
149
|
*,
|
|
105
150
|
command: str,
|
|
106
151
|
ctx: ToolExecutionContext,
|
|
107
|
-
policy_engine:
|
|
152
|
+
policy_engine: PolicyEngine | None,
|
|
108
153
|
) -> AuthorizationDecision:
|
|
109
154
|
"""roots 内 silent allow → ask(仅 cliArg 有 Bash(pattern) 且无裸 Bash 时)。"""
|
|
110
|
-
|
|
155
|
+
engine = resolve_default_policy_engine(policy_engine)
|
|
156
|
+
if engine is None or decision.behavior != "allow" or decision.policy_id:
|
|
111
157
|
return decision
|
|
112
158
|
registry = build_permission_rule_registry(
|
|
113
|
-
policy_config=
|
|
159
|
+
policy_config=engine._config,
|
|
114
160
|
session_store=ctx.session_store,
|
|
115
161
|
workspace_root=ctx.workspace_root,
|
|
116
162
|
agent_home_segment=ctx.agent_home_segment,
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/bash/shell_rule_matching.py — CC shellRuleMatching.ts 对齐(Phase D)
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
prefix :*、wildcard(含 \\* / \\\\ 转义)、``git *`` 尾 wildcard 可选参数语义。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
command_allow_rules.bash_command_matches_permission_rule。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from typing import Literal
|
|
16
|
+
|
|
17
|
+
_ESCAPED_STAR = "\x00ESCAPED_STAR\x00"
|
|
18
|
+
_ESCAPED_BACKSLASH = "\x00ESCAPED_BACKSLASH\x00"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def permission_rule_extract_prefix(permission_rule: str) -> str | None:
|
|
22
|
+
m = re.match(r"^(.+):\*$", permission_rule.strip())
|
|
23
|
+
return m.group(1) if m else None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def has_wildcards(pattern: str) -> bool:
|
|
27
|
+
text = pattern.strip()
|
|
28
|
+
if text.endswith(":*"):
|
|
29
|
+
return False
|
|
30
|
+
i = 0
|
|
31
|
+
while i < len(text):
|
|
32
|
+
if text[i] == "*":
|
|
33
|
+
backslashes = 0
|
|
34
|
+
j = i - 1
|
|
35
|
+
while j >= 0 and text[j] == "\\":
|
|
36
|
+
backslashes += 1
|
|
37
|
+
j -= 1
|
|
38
|
+
if backslashes % 2 == 0:
|
|
39
|
+
return True
|
|
40
|
+
i += 1
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def match_wildcard_pattern(
|
|
45
|
+
pattern: str,
|
|
46
|
+
command: str,
|
|
47
|
+
*,
|
|
48
|
+
case_insensitive: bool = False,
|
|
49
|
+
) -> bool:
|
|
50
|
+
"""对齐 CC ``matchWildcardPattern``(含 trailing `` *`` → optional args)。"""
|
|
51
|
+
trimmed = pattern.strip()
|
|
52
|
+
processed = ""
|
|
53
|
+
i = 0
|
|
54
|
+
while i < len(trimmed):
|
|
55
|
+
ch = trimmed[i]
|
|
56
|
+
if ch == "\\" and i + 1 < len(trimmed):
|
|
57
|
+
nxt = trimmed[i + 1]
|
|
58
|
+
if nxt == "*":
|
|
59
|
+
processed += _ESCAPED_STAR
|
|
60
|
+
i += 2
|
|
61
|
+
continue
|
|
62
|
+
if nxt == "\\":
|
|
63
|
+
processed += _ESCAPED_BACKSLASH
|
|
64
|
+
i += 2
|
|
65
|
+
continue
|
|
66
|
+
processed += ch
|
|
67
|
+
i += 1
|
|
68
|
+
|
|
69
|
+
escaped = re.sub(r"[.+?^${}()|\[\]\\'\"]", r"\\\g<0>", processed)
|
|
70
|
+
with_wildcards = escaped.replace("*", ".*")
|
|
71
|
+
regex_body = with_wildcards.replace(_ESCAPED_STAR, r"\*").replace(
|
|
72
|
+
_ESCAPED_BACKSLASH, r"\\"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
unescaped_stars = processed.count("*")
|
|
76
|
+
if regex_body.endswith(" .*") and unescaped_stars == 1:
|
|
77
|
+
regex_body = regex_body[:-3] + "( .*)?"
|
|
78
|
+
|
|
79
|
+
flags = re.DOTALL
|
|
80
|
+
if case_insensitive:
|
|
81
|
+
flags |= re.IGNORECASE
|
|
82
|
+
return re.fullmatch(f"^{regex_body}$", command, flags=flags) is not None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass(frozen=True)
|
|
86
|
+
class ShellPermissionRule:
|
|
87
|
+
kind: Literal["exact", "prefix", "wildcard"]
|
|
88
|
+
value: str
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def parse_permission_rule(rule_content: str) -> ShellPermissionRule:
|
|
92
|
+
text = rule_content.strip()
|
|
93
|
+
prefix = permission_rule_extract_prefix(text)
|
|
94
|
+
if prefix is not None:
|
|
95
|
+
return ShellPermissionRule(kind="prefix", value=prefix)
|
|
96
|
+
if has_wildcards(text):
|
|
97
|
+
return ShellPermissionRule(kind="wildcard", value=text)
|
|
98
|
+
return ShellPermissionRule(kind="exact", value=text)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def match_shell_permission_rule(
|
|
102
|
+
rule_content: str,
|
|
103
|
+
command: str,
|
|
104
|
+
*,
|
|
105
|
+
match_mode: Literal["exact", "prefix"] = "prefix",
|
|
106
|
+
is_compound: bool = False,
|
|
107
|
+
) -> bool:
|
|
108
|
+
"""单条 Bash 内容规则对单段 command 的匹配(CC ``bashPermissionRule`` 分支)。"""
|
|
109
|
+
cmd = command.strip()
|
|
110
|
+
if not cmd:
|
|
111
|
+
return False
|
|
112
|
+
parsed = parse_permission_rule(rule_content)
|
|
113
|
+
|
|
114
|
+
if parsed.kind == "exact":
|
|
115
|
+
return parsed.value == cmd
|
|
116
|
+
|
|
117
|
+
if parsed.kind == "prefix":
|
|
118
|
+
if match_mode == "exact":
|
|
119
|
+
return parsed.value == cmd
|
|
120
|
+
if is_compound:
|
|
121
|
+
return False
|
|
122
|
+
prefix = parsed.value
|
|
123
|
+
if cmd == prefix:
|
|
124
|
+
return True
|
|
125
|
+
if cmd.startswith(prefix + " "):
|
|
126
|
+
return True
|
|
127
|
+
xargs_prefix = "xargs " + prefix
|
|
128
|
+
if cmd == xargs_prefix or cmd.startswith(xargs_prefix + " "):
|
|
129
|
+
return True
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
if match_mode == "exact":
|
|
133
|
+
return False
|
|
134
|
+
if is_compound:
|
|
135
|
+
return False
|
|
136
|
+
return match_wildcard_pattern(parsed.value, cmd)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
__all__ = [
|
|
140
|
+
"match_shell_permission_rule",
|
|
141
|
+
"match_wildcard_pattern",
|
|
142
|
+
"parse_permission_rule",
|
|
143
|
+
]
|
|
@@ -1034,7 +1034,17 @@ class BashRuntimeTool(RuntimeTool):
|
|
|
1034
1034
|
return decision
|
|
1035
1035
|
|
|
1036
1036
|
final = self._authorize_sed_segment(command=command, ctx=ctx, cwd=cwd) or decision
|
|
1037
|
-
|
|
1037
|
+
final = promote_unmatched_cliarg_content_only_allow_to_ask(
|
|
1038
|
+
final,
|
|
1039
|
+
command=command,
|
|
1040
|
+
ctx=ctx,
|
|
1041
|
+
policy_engine=self._policy,
|
|
1042
|
+
)
|
|
1043
|
+
from langchain_agentx.tools.bash.command_allow_rules import (
|
|
1044
|
+
promote_unscoped_bash_passthrough_to_ask,
|
|
1045
|
+
)
|
|
1046
|
+
|
|
1047
|
+
return promote_unscoped_bash_passthrough_to_ask(
|
|
1038
1048
|
final,
|
|
1039
1049
|
command=command,
|
|
1040
1050
|
ctx=ctx,
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/skill/registry_permission.py — Skill 内容级 deny/allow(CC SkillTool.checkPermissions · Phase D)
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
Skill(name) / Skill(prefix:*) 与 registry 规则匹配。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
SkillRuntimeTool.run_l1_permission_check(frontmatter 约束之前)。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
14
|
+
|
|
15
|
+
from langchain_agentx.tool_runtime.models import AuthorizationDecision
|
|
16
|
+
from langchain_agentx.tool_runtime.permission_decision import (
|
|
17
|
+
attach_decision_reason,
|
|
18
|
+
make_other_decision_reason,
|
|
19
|
+
)
|
|
20
|
+
from langchain_agentx.tool_runtime.permission_rule_matcher import RegistryPermissionMatcher
|
|
21
|
+
from langchain_agentx.tool_runtime.permission_rules import build_permission_rule_registry
|
|
22
|
+
from langchain_agentx.tools.bash.shell_rule_matching import match_shell_permission_rule
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from langchain_agentx.tool_runtime.models import ToolExecutionContext
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def skill_permission_rule_content(skill_name: str) -> str:
|
|
29
|
+
text = skill_name.strip()
|
|
30
|
+
if text.startswith("/"):
|
|
31
|
+
text = text[1:]
|
|
32
|
+
return text
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def skill_rule_content_matches(*, skill_name: str, rule_content: str) -> bool:
|
|
36
|
+
"""对齐 CC ``ruleMatches``:精确或 ``prefix:*``。"""
|
|
37
|
+
command_name = skill_permission_rule_content(skill_name)
|
|
38
|
+
normalized_rule = rule_content.strip()
|
|
39
|
+
if normalized_rule.startswith("/"):
|
|
40
|
+
normalized_rule = normalized_rule[1:]
|
|
41
|
+
if normalized_rule == command_name:
|
|
42
|
+
return True
|
|
43
|
+
if normalized_rule.endswith(":*"):
|
|
44
|
+
prefix = normalized_rule[:-2]
|
|
45
|
+
return command_name.startswith(prefix)
|
|
46
|
+
return match_shell_permission_rule(
|
|
47
|
+
normalized_rule, command_name, match_mode="prefix"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def evaluate_skill_registry_permission(
|
|
52
|
+
skill_name: str,
|
|
53
|
+
ctx: ToolExecutionContext,
|
|
54
|
+
policy_engine: Any,
|
|
55
|
+
) -> AuthorizationDecision | None:
|
|
56
|
+
if policy_engine is None:
|
|
57
|
+
return None
|
|
58
|
+
registry = build_permission_rule_registry(
|
|
59
|
+
policy_config=policy_engine._config,
|
|
60
|
+
session_store=ctx.session_store,
|
|
61
|
+
workspace_root=ctx.workspace_root,
|
|
62
|
+
agent_home_segment=ctx.agent_home_segment,
|
|
63
|
+
)
|
|
64
|
+
target = skill_permission_rule_content(skill_name)
|
|
65
|
+
matcher = RegistryPermissionMatcher(registry)
|
|
66
|
+
for match_fn in (matcher.match_deny, matcher.match_ask, matcher.match_allow):
|
|
67
|
+
matched = match_fn("Skill", target)
|
|
68
|
+
if matched is not None:
|
|
69
|
+
return matched
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def skill_default_ask_decision(skill_name: str) -> AuthorizationDecision:
|
|
74
|
+
target = skill_permission_rule_content(skill_name)
|
|
75
|
+
suggestions = (
|
|
76
|
+
{
|
|
77
|
+
"type": "addRules",
|
|
78
|
+
"destination": "localSettings",
|
|
79
|
+
"behavior": "allow",
|
|
80
|
+
"rules": [{"toolName": "Skill", "ruleContent": target}],
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
decision = AuthorizationDecision(
|
|
84
|
+
behavior="ask",
|
|
85
|
+
ask_prompt=f"Allow skill {target}?",
|
|
86
|
+
policy_id="skill_default",
|
|
87
|
+
metadata={"suggestions": suggestions},
|
|
88
|
+
)
|
|
89
|
+
return attach_decision_reason(
|
|
90
|
+
decision,
|
|
91
|
+
make_other_decision_reason("Skill default permission prompt"),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
__all__ = [
|
|
96
|
+
"evaluate_skill_registry_permission",
|
|
97
|
+
"skill_default_ask_decision",
|
|
98
|
+
"skill_rule_content_matches",
|
|
99
|
+
]
|
|
@@ -61,6 +61,26 @@ def resolve_managed_skills_dir(agent_home_segment: str) -> Path:
|
|
|
61
61
|
return resolve_managed_settings_root() / segment / "skills"
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
def resolve_skill_read_roots(
|
|
65
|
+
workspace_cfg: ResolvedAgentStateConfig,
|
|
66
|
+
*,
|
|
67
|
+
project_skills_dir: Path | None = None,
|
|
68
|
+
) -> tuple[Path, ...]:
|
|
69
|
+
"""Skill 发现目录绝对路径(与 ``resolve_skill_source_specs`` 同源,去重保序)。"""
|
|
70
|
+
seen: set[Path] = set()
|
|
71
|
+
roots: list[Path] = []
|
|
72
|
+
for spec in resolve_skill_source_specs(
|
|
73
|
+
workspace_cfg,
|
|
74
|
+
project_skills_dir=project_skills_dir,
|
|
75
|
+
):
|
|
76
|
+
resolved = spec.skills_dir.expanduser().resolve()
|
|
77
|
+
if resolved in seen:
|
|
78
|
+
continue
|
|
79
|
+
seen.add(resolved)
|
|
80
|
+
roots.append(resolved)
|
|
81
|
+
return tuple(roots)
|
|
82
|
+
|
|
83
|
+
|
|
64
84
|
def resolve_skill_source_specs(
|
|
65
85
|
workspace_cfg: ResolvedAgentStateConfig,
|
|
66
86
|
*,
|
|
@@ -83,6 +103,7 @@ __all__ = [
|
|
|
83
103
|
"SourcedSkillEntry",
|
|
84
104
|
"policy_skills_disabled",
|
|
85
105
|
"resolve_managed_skills_dir",
|
|
106
|
+
"resolve_skill_read_roots",
|
|
86
107
|
"resolve_skill_source_specs",
|
|
87
108
|
"resolve_user_skills_dir",
|
|
88
109
|
]
|
|
@@ -187,6 +187,24 @@ class SkillRuntimeTool(RuntimeTool):
|
|
|
187
187
|
),
|
|
188
188
|
)
|
|
189
189
|
|
|
190
|
+
from langchain_agentx.tools.skill.registry_permission import (
|
|
191
|
+
evaluate_skill_registry_permission,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
registry_decision = evaluate_skill_registry_permission(
|
|
195
|
+
inp.skill_name,
|
|
196
|
+
ctx,
|
|
197
|
+
self._policy,
|
|
198
|
+
)
|
|
199
|
+
if registry_decision is not None:
|
|
200
|
+
if registry_decision.behavior == "allow":
|
|
201
|
+
pass
|
|
202
|
+
else:
|
|
203
|
+
return L1PermissionResult(
|
|
204
|
+
outcome=registry_decision.behavior,
|
|
205
|
+
decision=registry_decision,
|
|
206
|
+
)
|
|
207
|
+
|
|
190
208
|
local = self._constraint_evaluator.evaluate(cmd.frontmatter)
|
|
191
209
|
if local.behavior != "allow":
|
|
192
210
|
return L1PermissionResult(outcome=local.behavior, decision=local)
|
|
@@ -40,8 +40,32 @@ def webfetch_permission_rule_content(url: str) -> str:
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def webfetch_rule_content_matches(*, target: str, rule_content: str) -> bool:
|
|
43
|
-
"""WebFetch
|
|
44
|
-
|
|
43
|
+
"""WebFetch 内容规则:``domain:host`` 精确或 ``domain:*.example.com`` wildcard(Phase D)。"""
|
|
44
|
+
t = target.strip().lower()
|
|
45
|
+
r = rule_content.strip().lower()
|
|
46
|
+
if t == r:
|
|
47
|
+
return True
|
|
48
|
+
if not r.startswith("domain:") or not t.startswith("domain:"):
|
|
49
|
+
return False
|
|
50
|
+
host_pattern = r[len("domain:") :]
|
|
51
|
+
host_target = t[len("domain:") :]
|
|
52
|
+
if "*" not in host_pattern and "?" not in host_pattern:
|
|
53
|
+
return False
|
|
54
|
+
from langchain_agentx.tools.bash.shell_rule_matching import match_wildcard_pattern
|
|
55
|
+
|
|
56
|
+
return match_wildcard_pattern(host_pattern, host_target)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def webfetch_allow_suggestions(rule_content: str) -> tuple[dict[str, object], ...]:
|
|
60
|
+
"""对齐 CC WebFetchTool ``buildSuggestions``(L3 ``metadata.suggestions``)。"""
|
|
61
|
+
return (
|
|
62
|
+
{
|
|
63
|
+
"type": "addRules",
|
|
64
|
+
"destination": "localSettings",
|
|
65
|
+
"behavior": "allow",
|
|
66
|
+
"rules": [{"toolName": "WebFetch", "ruleContent": rule_content}],
|
|
67
|
+
},
|
|
68
|
+
)
|
|
45
69
|
|
|
46
70
|
|
|
47
71
|
def evaluate_webfetch_registry_permission(
|
|
@@ -60,6 +84,16 @@ def evaluate_webfetch_registry_permission(
|
|
|
60
84
|
for match_fn in (matcher.match_deny, matcher.match_ask, matcher.match_allow):
|
|
61
85
|
matched = match_fn("WebFetch", target)
|
|
62
86
|
if matched is not None:
|
|
87
|
+
if matched.behavior == "ask":
|
|
88
|
+
meta = dict(matched.metadata or {})
|
|
89
|
+
meta.setdefault("suggestions", webfetch_allow_suggestions(target))
|
|
90
|
+
return AuthorizationDecision(
|
|
91
|
+
behavior=matched.behavior,
|
|
92
|
+
message=matched.message,
|
|
93
|
+
policy_id=matched.policy_id,
|
|
94
|
+
ask_prompt=matched.ask_prompt,
|
|
95
|
+
metadata=meta,
|
|
96
|
+
)
|
|
63
97
|
return matched
|
|
64
98
|
decision = AuthorizationDecision(
|
|
65
99
|
behavior="ask",
|
|
@@ -67,6 +101,7 @@ def evaluate_webfetch_registry_permission(
|
|
|
67
101
|
"Claude requested permissions to use WebFetch, but you haven't granted it yet."
|
|
68
102
|
),
|
|
69
103
|
policy_id="webfetch_default",
|
|
104
|
+
metadata={"suggestions": webfetch_allow_suggestions(target)},
|
|
70
105
|
)
|
|
71
106
|
return attach_decision_reason(
|
|
72
107
|
decision,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
langchain_agentx/__init__.py,sha256=
|
|
1
|
+
langchain_agentx/__init__.py,sha256=89oyf7kdDyMvgQvOwz2Ud0jqlfoa9LoiieytUKz2w0w,1614
|
|
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
|
|
@@ -68,7 +68,7 @@ langchain_agentx/loop/exit/terminal.py,sha256=_DAZk-kS4Xd0nxMlayLo60-uj1Y5ozgBjL
|
|
|
68
68
|
langchain_agentx/loop/exit/withheld_error.py,sha256=LfX3j7Rgd9VoMzlz7GOs9TPuRe7RXr_Gxweyotx3tqQ,1846
|
|
69
69
|
langchain_agentx/loop/graph/__init__.py,sha256=9pUUqn7G87n3lV45iYIU24j0wcTHyCD--SSdtv3urh4,136
|
|
70
70
|
langchain_agentx/loop/graph/builtin_loop_control.py,sha256=31o4H6SDGC_vECSliXecuVEnzlD1ahlVZ5JzEpxYY04,9248
|
|
71
|
-
langchain_agentx/loop/graph/factory.py,sha256=
|
|
71
|
+
langchain_agentx/loop/graph/factory.py,sha256=mw2nJUddW5uV2oEi0-tt59eJ65JNQVZLM4pCwINyAY4,104396
|
|
72
72
|
langchain_agentx/loop/graph/graph_edges.py,sha256=kW6Npx-nMIPzcaisCxVPkTwf5Y9GOKy1NWCNkJWeJBQ,47370
|
|
73
73
|
langchain_agentx/loop/graph/reactive_compact_node.py,sha256=633b5hTnR3BK8GBEKWhzOwGB0BQrVRKozNxDomWFjLo,8762
|
|
74
74
|
langchain_agentx/loop/graph/runtime_tools_node.py,sha256=wQkbc0ophyefpPZEc0q1vKoJOIdS4S6d6hSsrkdRLDk,7387
|
|
@@ -404,10 +404,10 @@ langchain_agentx/tool_runtime/denial_tracking_bridge.py,sha256=qssnZ_tzCIrDeJvtx
|
|
|
404
404
|
langchain_agentx/tool_runtime/errors.py,sha256=Ed1FMjBdPfr0WG-YwLIm_7jokl9k_DjHFNiewZTeQfg,8883
|
|
405
405
|
langchain_agentx/tool_runtime/failure_event_emitter.py,sha256=C7c4Gc7Vo_R6_ah2Xxa2eso7l9Iuz_KTgRV0j0B08rA,10467
|
|
406
406
|
langchain_agentx/tool_runtime/identical_call_cache.py,sha256=v8WWuTHWR5ThujcMO4Ty28mZS_w-JN24267fZ4PHWK8,3865
|
|
407
|
-
langchain_agentx/tool_runtime/inner_permission_chain.py,sha256=
|
|
407
|
+
langchain_agentx/tool_runtime/inner_permission_chain.py,sha256=50wYXRu7Hi-rvDiI2QxSLC4uBAsJwG3Vp-v3AutLY4E,17629
|
|
408
408
|
langchain_agentx/tool_runtime/limits.py,sha256=imE1IetmyKQM82INR0VkP3qZNM4bjUJuf1CG5BkY0uw,356
|
|
409
|
-
langchain_agentx/tool_runtime/loader.py,sha256=
|
|
410
|
-
langchain_agentx/tool_runtime/loop_loader.py,sha256=
|
|
409
|
+
langchain_agentx/tool_runtime/loader.py,sha256=vT1_1QoBCpDGhe9sWRqgDZBcuErF35rAcOAENDrUcCI,9452
|
|
410
|
+
langchain_agentx/tool_runtime/loop_loader.py,sha256=H8QKRmoi3ni-8vkyx90W2zfC4quDOfnNTc3d4ote4hM,9615
|
|
411
411
|
langchain_agentx/tool_runtime/loop_runtime_context.py,sha256=HBZMdtKD7MpU8o7JmURFZGPTfxr2aFFlFF2nBZR_n8s,3613
|
|
412
412
|
langchain_agentx/tool_runtime/messages.py,sha256=Fp-ZiLAbOQAQa8uIv-owE8PXtWyhpYSXxtzy3PVbKSw,3631
|
|
413
413
|
langchain_agentx/tool_runtime/models.py,sha256=cO9EgLKPXiktHl_xXtBwLQrCo6mj4tbT4WoPmuFLE0w,20345
|
|
@@ -419,14 +419,14 @@ langchain_agentx/tool_runtime/permission_context.py,sha256=ReVV2kjVWAPTsPaYXxRB-
|
|
|
419
419
|
langchain_agentx/tool_runtime/permission_decision.py,sha256=-dWuC60MV6PYgHHOsSxr6Qad8Mm3qgMuh6nkzM87LO4,10280
|
|
420
420
|
langchain_agentx/tool_runtime/permission_orchestrator.py,sha256=nwx76k74aAFgo63tfkoi6zUJTj9-10tnQzj03VYqFuw,4637
|
|
421
421
|
langchain_agentx/tool_runtime/permission_rule_matcher.py,sha256=z4VesjyvvEry-L2U1VsrJB3SR6wtaG8OOuhvftsvj9U,4725
|
|
422
|
-
langchain_agentx/tool_runtime/permission_rules.py,sha256=
|
|
422
|
+
langchain_agentx/tool_runtime/permission_rules.py,sha256=2N66xdR9khrzRY_1OCKsv05oAwWyf45e546NUwC_7hg,19709
|
|
423
423
|
langchain_agentx/tool_runtime/permission_settings_loader.py,sha256=Xu6dso0ghh4pb7Puf8nj20nXxjGeQvL4kIGycavwG_o,5624
|
|
424
424
|
langchain_agentx/tool_runtime/permission_settings_sync.py,sha256=g6TvjWwa7D0kw5rDznPsoMv7xylESPOyWA2vZW4HDm0,4590
|
|
425
425
|
langchain_agentx/tool_runtime/permission_snapshot.py,sha256=xFoi9fIcZjSw_zrIoyglneM-vJsDOLCEjiVoazr5Ack,1947
|
|
426
426
|
langchain_agentx/tool_runtime/pipeline.py,sha256=cNC8FzeBtfNo8I02wXsaBbbpwxbVXu67eRnr_OfEU3Y,53104
|
|
427
|
-
langchain_agentx/tool_runtime/policy.py,sha256=
|
|
427
|
+
langchain_agentx/tool_runtime/policy.py,sha256=CZvkFS9m28q1txTz5MTltKGouMQZJnRVyz8DEfRewYA,30892
|
|
428
428
|
langchain_agentx/tool_runtime/policy_decorator.py,sha256=XWbgh_TRlPJ-J-RFo1FKjIN_n_Z9rtLTfSwBQoyLTTg,2264
|
|
429
|
-
langchain_agentx/tool_runtime/process_loader.py,sha256=
|
|
429
|
+
langchain_agentx/tool_runtime/process_loader.py,sha256=qpgfRIla7Tyvbf9FcwGo5Z8x_ymNdMBxrx7OrZAQpzA,6889
|
|
430
430
|
langchain_agentx/tool_runtime/prompt_context.py,sha256=Dw3Qs8yWwT7dv-ZVHKnQxtPPKQXGySOQpwH4sstfsjE,3233
|
|
431
431
|
langchain_agentx/tool_runtime/read_ignore_patterns.py,sha256=l4kNulVDaOdQxcs4FeC2DT6k3172D4hgeafEbqGGvTw,3034
|
|
432
432
|
langchain_agentx/tool_runtime/read_permission.py,sha256=rwH7NEtL57aUKXVjSf6hMOOvEWOayNFyvI_SNoNCHPA,9284
|
|
@@ -532,9 +532,10 @@ langchain_agentx/tools/bash/ast_security.py,sha256=6DFd_TwT986dbGkKT3UnDIZioBdmi
|
|
|
532
532
|
langchain_agentx/tools/bash/auto_mode_adapter.py,sha256=7-yb_kGmpytBYkD2Hn5YWPIceo_YN4PatEKj8O7HKsM,4243
|
|
533
533
|
langchain_agentx/tools/bash/backend.py,sha256=E0DMBAENZLUSLvuEh6RubNJ6iCVnx1d2VGsQzVuLA5k,65361
|
|
534
534
|
langchain_agentx/tools/bash/bash_hardening.py,sha256=KEYKRC7kS_to4mzXuChvCmOIIb4dSUkOb9K1MBDtWDc,29296
|
|
535
|
+
langchain_agentx/tools/bash/bash_registry_content.py,sha256=YHE3PwuLBeFrEux4xNaDZjJpYtUSMFPLYr6A7g-fFxk,3569
|
|
535
536
|
langchain_agentx/tools/bash/bash_runtime_contract.py,sha256=s8vrvMl7405KLGw2hPFbzlzWohm5JX3c9cImMBgqh8I,1511
|
|
536
537
|
langchain_agentx/tools/bash/boundary_check.py,sha256=k9Bv-baIAUDE3-c_Brw3VSXXYMv_g3lTM0mabIadnwc,8822
|
|
537
|
-
langchain_agentx/tools/bash/command_allow_rules.py,sha256
|
|
538
|
+
langchain_agentx/tools/bash/command_allow_rules.py,sha256=aIXxnb4tD3fDWqADTKAgNaN5DP8TqbMeFw0NxfFtFQQ,7562
|
|
538
539
|
langchain_agentx/tools/bash/cwd_reporter.py,sha256=mASbKWLb4MiX4zLKY3w1ocIdfhmwj5KRgX4OW_PcAns,2642
|
|
539
540
|
langchain_agentx/tools/bash/events.py,sha256=xDpPA3zhne2cnlYTmiiPTsV423-h-Nw-VKDR_rlnbGE,6065
|
|
540
541
|
langchain_agentx/tools/bash/l1_decision_reason.py,sha256=MOL9HBBpJEgypuTiYdNojQBl8eIsTwRge5CtGeYRJKI,5365
|
|
@@ -557,8 +558,9 @@ langchain_agentx/tools/bash/semantic_matrix.py,sha256=stvsudu8mGjS1JJCawMAUKabt-
|
|
|
557
558
|
langchain_agentx/tools/bash/semantics.py,sha256=pkf4URTulAOVL7m04Rq9JJF7z0J2mTEsGWrjyoY9r6o,5769
|
|
558
559
|
langchain_agentx/tools/bash/shell_locator.py,sha256=AUnCZS8gmZurCs8rL4y0MMOS9UZimfg5rQ5At38mZKU,6296
|
|
559
560
|
langchain_agentx/tools/bash/shell_quoting.py,sha256=K4JLVGwU8VpdQzfQB1o3xsq4HoCKHew1pry8hHChcoA,4498
|
|
561
|
+
langchain_agentx/tools/bash/shell_rule_matching.py,sha256=uYEKg2C5M1r0eh3L4b5NCML1Gj2UBdIXeL_u2A6_RyM,3981
|
|
560
562
|
langchain_agentx/tools/bash/task_runtime.py,sha256=hikVC9YugtvpLf4nDaPlX_GFem1YQvDRwN0ShRflUYU,3568
|
|
561
|
-
langchain_agentx/tools/bash/tool.py,sha256=
|
|
563
|
+
langchain_agentx/tools/bash/tool.py,sha256=O8wDLMsPM9ryx_BfKo_SaGtlyRb7UyUxDAmOEgUgIek,46699
|
|
562
564
|
langchain_agentx/tools/bash/windows_shell_quoting.py,sha256=0RzLnTKyFrJ536QzqPZvSyUcQWN794XRlAq2RdHeKHo,1615
|
|
563
565
|
langchain_agentx/tools/edit/__init__.py,sha256=PiiOHUODUzTRQxxPdE7ZMTjd3vfdzm9LEcn1lpVtg8o,203
|
|
564
566
|
langchain_agentx/tools/edit/backend.py,sha256=JrikYafZDGMQ2JjXRxbnv1VSHVGRUdLmZN__nnqkM9o,3159
|
|
@@ -618,9 +620,10 @@ langchain_agentx/tools/skill/paths_matcher.py,sha256=J4SOBThray_72mrYiQUfJ5Icu6T
|
|
|
618
620
|
langchain_agentx/tools/skill/plugin_loader.py,sha256=qT0VcIWkKFmRGGH8-e_f4y1XIpjY71TVbnZwnu2M9_M,1191
|
|
619
621
|
langchain_agentx/tools/skill/policy.py,sha256=SFlW627gyo_-wyIppAqoytBmrLPlSGw-mbR00gjOJQY,1541
|
|
620
622
|
langchain_agentx/tools/skill/prompt.py,sha256=tvSqvCGroVdloveiWIYxIPQmpESqzPAs5ZJztvHBdOI,1782
|
|
623
|
+
langchain_agentx/tools/skill/registry_permission.py,sha256=ns_S6IBOdacrZcgHzCOj9rjlPOIecavF5hW-pHmalcY,3258
|
|
621
624
|
langchain_agentx/tools/skill/repository_factory.py,sha256=7zDoPDZgxoR03CkmKdx9b0aUXwaH8E5LtBYQPGF26rA,1222
|
|
622
|
-
langchain_agentx/tools/skill/source_paths.py,sha256=
|
|
623
|
-
langchain_agentx/tools/skill/tool.py,sha256=
|
|
625
|
+
langchain_agentx/tools/skill/source_paths.py,sha256=2CeVYF37JONiXrKQcogdBs0Y31p9AAMIOgGXZKnBQfE,3651
|
|
626
|
+
langchain_agentx/tools/skill/tool.py,sha256=vhiwjeyST9BhsMZmKk8-Vej3JId0AxTPrQ5ebFvguG4,19711
|
|
624
627
|
langchain_agentx/tools/skill/bundled/__init__.py,sha256=gaCH_mROpv0lIgLjin1VIFu-8hbP2nY1PHoF7vZt2Ns,4516
|
|
625
628
|
langchain_agentx/tools/skill/bundled/prompts.py,sha256=gcaTnNRlWfHNPgO3EfZxU9vMYbOapMrAduvgaVc-_Fc,4838
|
|
626
629
|
langchain_agentx/tools/skill/bundled/registry.py,sha256=oxvvFeeUzM-qVPtayVaQp5tfRiMazyln2-rCM86AYa8,6274
|
|
@@ -650,7 +653,7 @@ langchain_agentx/tools/webfetch/loader.py,sha256=QCATTqBvQHXiMVuAbGQGQeymVevDfWw
|
|
|
650
653
|
langchain_agentx/tools/webfetch/models.py,sha256=e_k3FS_9JLekKsq1auRioLDhUFz4YNUlX3MrvnW5BE0,2561
|
|
651
654
|
langchain_agentx/tools/webfetch/preapproved.py,sha256=8SjbOZdZla208oml8pzixkA-vvZ9c0kofCQ-3XQCoeQ,4312
|
|
652
655
|
langchain_agentx/tools/webfetch/prompt.py,sha256=lpiAV9ftjs-M1BS6R5_L_uNAcOPJ5bJ9H-ZE2eq_5OY,3771
|
|
653
|
-
langchain_agentx/tools/webfetch/registry_permission.py,sha256=
|
|
656
|
+
langchain_agentx/tools/webfetch/registry_permission.py,sha256=lkJx0XhY6XOeYgM3uAsFiFEtWawE5mZMl8IyuQchYSo,3924
|
|
654
657
|
langchain_agentx/tools/webfetch/summary.py,sha256=wk-5C39VoEIj9OyZtw6DOs6GYtdvPVECb80uGgVwI7w,1038
|
|
655
658
|
langchain_agentx/tools/webfetch/tool.py,sha256=4qT0nH2DwC8iPEWzmnZjns8lnynXS7muc-2xCgVlbZY,9199
|
|
656
659
|
langchain_agentx/tools/websearch/__init__.py,sha256=gOVwLv3X9iLUpnN5lhyLeEiovw-JDmzdqxGg7ufkIMY,950
|
|
@@ -763,8 +766,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
|
|
|
763
766
|
langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
|
|
764
767
|
langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
|
|
765
768
|
langchain_agentx/workspace/view.py,sha256=PGasqTaqhlD03SXXazHuw4RHfV681AIdlsYqL70pEjc,4774
|
|
766
|
-
langchain_agentx_python-2.
|
|
767
|
-
langchain_agentx_python-2.
|
|
768
|
-
langchain_agentx_python-2.
|
|
769
|
-
langchain_agentx_python-2.
|
|
770
|
-
langchain_agentx_python-2.
|
|
769
|
+
langchain_agentx_python-2.3.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
770
|
+
langchain_agentx_python-2.3.1.dist-info/METADATA,sha256=Ww83WPubbcBJqsMh8jSNRJhu7Wcx9XxRyz0AvqS1c-E,24250
|
|
771
|
+
langchain_agentx_python-2.3.1.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
|
|
772
|
+
langchain_agentx_python-2.3.1.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
|
|
773
|
+
langchain_agentx_python-2.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{langchain_agentx_python-2.2.9.dist-info → langchain_agentx_python-2.3.1.dist-info}/top_level.txt
RENAMED
|
File without changes
|