langchain-agentx-python 2.2.7__py3-none-any.whl → 2.2.9__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/tool_runtime/__init__.py +3 -0
- langchain_agentx/tool_runtime/inner_permission_chain.py +12 -0
- langchain_agentx/tool_runtime/permission_cli_rules.py +143 -0
- langchain_agentx/tool_runtime/permission_rule_matcher.py +13 -1
- langchain_agentx/tool_runtime/permission_rules.py +32 -2
- langchain_agentx/tools/ask_user_question/constants.py +2 -2
- langchain_agentx/tools/ask_user_question/validators.py +7 -5
- langchain_agentx/tools/bash/command_allow_rules.py +76 -3
- langchain_agentx/tools/bash/tool.py +27 -3
- langchain_agentx/tools/webfetch/registry_permission.py +74 -0
- langchain_agentx/tools/webfetch/tool.py +12 -1
- langchain_agentx/tools/websearch/tool.py +17 -1
- {langchain_agentx_python-2.2.7.dist-info → langchain_agentx_python-2.2.9.dist-info}/METADATA +1 -1
- {langchain_agentx_python-2.2.7.dist-info → langchain_agentx_python-2.2.9.dist-info}/RECORD +18 -16
- {langchain_agentx_python-2.2.7.dist-info → langchain_agentx_python-2.2.9.dist-info}/LICENSE +0 -0
- {langchain_agentx_python-2.2.7.dist-info → langchain_agentx_python-2.2.9.dist-info}/WHEEL +0 -0
- {langchain_agentx_python-2.2.7.dist-info → langchain_agentx_python-2.2.9.dist-info}/top_level.txt +0 -0
langchain_agentx/__init__.py
CHANGED
|
@@ -43,6 +43,7 @@ from .permission_snapshot import (
|
|
|
43
43
|
collect_denied_agent_types_from_policy as collect_denied_agent_types,
|
|
44
44
|
)
|
|
45
45
|
from .path_safety import PathSafetyConfig, PathSafetyEvaluator
|
|
46
|
+
from .permission_cli_rules import apply_cli_permission_rules, parse_tool_list_from_cli
|
|
46
47
|
from .policy import DefaultPolicyEngine, PolicyEngine, ToolPolicyConfig
|
|
47
48
|
from .read_ignore_patterns import (
|
|
48
49
|
ReadDenyPatternCollector,
|
|
@@ -122,6 +123,8 @@ __all__ = [
|
|
|
122
123
|
"validation_error_envelope",
|
|
123
124
|
# policy
|
|
124
125
|
"ToolPolicyConfig",
|
|
126
|
+
"apply_cli_permission_rules",
|
|
127
|
+
"parse_tool_list_from_cli",
|
|
125
128
|
"PolicyEngine",
|
|
126
129
|
"DefaultPolicyEngine",
|
|
127
130
|
"PolicyEngineDecorator",
|
|
@@ -24,6 +24,7 @@ from .models import AuthorizationDecision
|
|
|
24
24
|
from .permission_decision import (
|
|
25
25
|
POLICY_ID_PREFIX_ALWAYS_ASK_TOOL,
|
|
26
26
|
attach_decision_reason,
|
|
27
|
+
get_decision_reason,
|
|
27
28
|
make_other_decision_reason,
|
|
28
29
|
make_rule_decision_reason,
|
|
29
30
|
)
|
|
@@ -40,6 +41,14 @@ if TYPE_CHECKING:
|
|
|
40
41
|
from .policy import DefaultPolicyEngine
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
def _l1_ask_blocks_tool_level_allow(decision: AuthorizationDecision) -> bool:
|
|
45
|
+
"""CC step 1g:safetyCheck 产生的 ask 不得被段 4 tool-level allow 覆盖。"""
|
|
46
|
+
reason = get_decision_reason(decision)
|
|
47
|
+
if reason is None:
|
|
48
|
+
return False
|
|
49
|
+
return reason.get("type") == "safetyCheck"
|
|
50
|
+
|
|
51
|
+
|
|
43
52
|
@dataclass
|
|
44
53
|
class PathPolicyResult:
|
|
45
54
|
decision: AuthorizationDecision | None = None
|
|
@@ -80,6 +89,9 @@ class InnerPermissionChain:
|
|
|
80
89
|
raise ValueError(f"L1 outcome={l1.outcome!r} requires decision")
|
|
81
90
|
if tool.requires_user_interaction_for(data, ctx):
|
|
82
91
|
return l1.decision
|
|
92
|
+
# CC step 1g:safetyCheck ask 不被 tool-level allow(2b)覆盖
|
|
93
|
+
if _l1_ask_blocks_tool_level_allow(l1.decision):
|
|
94
|
+
return l1.decision
|
|
83
95
|
seg4 = self._segment_4_mode_shortcircuit(tool, data, ctx, tool_flags)
|
|
84
96
|
if seg4 is not None:
|
|
85
97
|
return seg4
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
permission_cli_rules.py — CC 对齐 CLI allow/disallow 解析与 ToolPolicyConfig 注入
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
parse_tool_list_from_cli(对齐 CC parseToolListFromCLI);
|
|
6
|
+
apply_cli_permission_rules 写入 allow_rules_by_source / deny_rules_by_source["cliArg"]。
|
|
7
|
+
|
|
8
|
+
链路位置:
|
|
9
|
+
CLI / new_agent / stream_ui → ToolRuntimeLoader(policy_config=...) → build_permission_rule_registry。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from collections.abc import Sequence
|
|
15
|
+
|
|
16
|
+
from .permission_rules import (
|
|
17
|
+
PermissionRule,
|
|
18
|
+
PermissionRuleBehavior,
|
|
19
|
+
PermissionRuleSource,
|
|
20
|
+
PermissionRuleValue,
|
|
21
|
+
permission_rule_to_string,
|
|
22
|
+
permission_rule_value_from_string,
|
|
23
|
+
)
|
|
24
|
+
from .policy import ToolPolicyConfig
|
|
25
|
+
|
|
26
|
+
_LEGACY_TASK_TOOL = "Task"
|
|
27
|
+
_CANONICAL_AGENT_TOOL = "Agent"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def parse_tool_list_from_cli(tools: Sequence[str]) -> list[str]:
|
|
31
|
+
"""对齐 CC permissionSetup.ts parseToolListFromCLI(括号内不切分)。"""
|
|
32
|
+
if not tools:
|
|
33
|
+
return []
|
|
34
|
+
result: list[str] = []
|
|
35
|
+
for tool_string in tools:
|
|
36
|
+
if not tool_string:
|
|
37
|
+
continue
|
|
38
|
+
current = ""
|
|
39
|
+
is_in_parens = False
|
|
40
|
+
for char in tool_string:
|
|
41
|
+
if char == "(":
|
|
42
|
+
is_in_parens = True
|
|
43
|
+
current += char
|
|
44
|
+
elif char == ")":
|
|
45
|
+
is_in_parens = False
|
|
46
|
+
current += char
|
|
47
|
+
elif char == ",":
|
|
48
|
+
if is_in_parens:
|
|
49
|
+
current += char
|
|
50
|
+
else:
|
|
51
|
+
if current.strip():
|
|
52
|
+
result.append(current.strip())
|
|
53
|
+
current = ""
|
|
54
|
+
elif char == " ":
|
|
55
|
+
if is_in_parens:
|
|
56
|
+
current += char
|
|
57
|
+
elif current.strip():
|
|
58
|
+
result.append(current.strip())
|
|
59
|
+
current = ""
|
|
60
|
+
else:
|
|
61
|
+
current += char
|
|
62
|
+
if current.strip():
|
|
63
|
+
result.append(current.strip())
|
|
64
|
+
return result
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def canonical_cli_rule_string(rule: str) -> str:
|
|
68
|
+
"""对齐 CC allow 列表规范化(permissionRuleValueFromString + ToString;Task→Agent)。"""
|
|
69
|
+
value = permission_rule_value_from_string(rule.strip())
|
|
70
|
+
tool_name = value.tool_name
|
|
71
|
+
if tool_name == _LEGACY_TASK_TOOL:
|
|
72
|
+
tool_name = _CANONICAL_AGENT_TOOL
|
|
73
|
+
normalized = PermissionRule(
|
|
74
|
+
source=PermissionRuleSource.CLI_ARG,
|
|
75
|
+
rule_behavior=PermissionRuleBehavior.ALLOW,
|
|
76
|
+
rule_value=PermissionRuleValue(
|
|
77
|
+
tool_name=tool_name,
|
|
78
|
+
rule_content=value.rule_content,
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
return permission_rule_to_string(normalized)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def apply_cli_permission_rules(
|
|
85
|
+
config: ToolPolicyConfig,
|
|
86
|
+
*,
|
|
87
|
+
allowed: Sequence[str] | None = None,
|
|
88
|
+
disallowed: Sequence[str] | None = None,
|
|
89
|
+
) -> ToolPolicyConfig:
|
|
90
|
+
"""
|
|
91
|
+
合并 cliArg 规则;不修改 userSettings / projectSettings 等其它 source。
|
|
92
|
+
|
|
93
|
+
``allowed is None`` / ``disallowed is None`` 表示不触碰对应 cliArg 键;
|
|
94
|
+
传空序列表示清除该 cliArg 键。
|
|
95
|
+
"""
|
|
96
|
+
allow_map = dict(config.allow_rules_by_source)
|
|
97
|
+
deny_map = dict(config.deny_rules_by_source)
|
|
98
|
+
|
|
99
|
+
if allowed is not None:
|
|
100
|
+
if allowed:
|
|
101
|
+
parsed = parse_tool_list_from_cli(allowed)
|
|
102
|
+
allow_map[PermissionRuleSource.CLI_ARG.value] = tuple(
|
|
103
|
+
canonical_cli_rule_string(r) for r in parsed
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
allow_map.pop(PermissionRuleSource.CLI_ARG.value, None)
|
|
107
|
+
|
|
108
|
+
if disallowed is not None:
|
|
109
|
+
if disallowed:
|
|
110
|
+
parsed = parse_tool_list_from_cli(disallowed)
|
|
111
|
+
deny_map[PermissionRuleSource.CLI_ARG.value] = tuple(
|
|
112
|
+
canonical_cli_rule_string(r) for r in parsed
|
|
113
|
+
)
|
|
114
|
+
else:
|
|
115
|
+
deny_map.pop(PermissionRuleSource.CLI_ARG.value, None)
|
|
116
|
+
|
|
117
|
+
return ToolPolicyConfig(
|
|
118
|
+
read_roots=list(config.read_roots),
|
|
119
|
+
write_roots=list(config.write_roots),
|
|
120
|
+
deny_globs=list(config.deny_globs),
|
|
121
|
+
read_deny_globs=list(config.read_deny_globs),
|
|
122
|
+
ask_globs=list(config.ask_globs),
|
|
123
|
+
always_ask_tools=config.always_ask_tools,
|
|
124
|
+
allow_rules_by_source=allow_map,
|
|
125
|
+
deny_rules_by_source=deny_map,
|
|
126
|
+
ask_rules_by_source=dict(config.ask_rules_by_source),
|
|
127
|
+
cli_allow_tools=config.cli_allow_tools,
|
|
128
|
+
flag_settings_path=config.flag_settings_path,
|
|
129
|
+
read_only_mode=config.read_only_mode,
|
|
130
|
+
enable_builtin_deny=config.enable_builtin_deny,
|
|
131
|
+
enable_path_safety=config.enable_path_safety,
|
|
132
|
+
path_safety=config.path_safety,
|
|
133
|
+
auto_mode=config.auto_mode,
|
|
134
|
+
project_memory_layout=config.project_memory_layout,
|
|
135
|
+
denial_tracking=config.denial_tracking,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
__all__ = [
|
|
140
|
+
"apply_cli_permission_rules",
|
|
141
|
+
"canonical_cli_rule_string",
|
|
142
|
+
"parse_tool_list_from_cli",
|
|
143
|
+
]
|
|
@@ -54,6 +54,13 @@ class RegistryPermissionMatcher:
|
|
|
54
54
|
) -> AuthorizationDecision | None:
|
|
55
55
|
return self._match(tool_name, target, PermissionRuleBehavior.ASK)
|
|
56
56
|
|
|
57
|
+
def match_allow(
|
|
58
|
+
self,
|
|
59
|
+
tool_name: str,
|
|
60
|
+
target: str,
|
|
61
|
+
) -> AuthorizationDecision | None:
|
|
62
|
+
return self._match(tool_name, target, PermissionRuleBehavior.ALLOW)
|
|
63
|
+
|
|
57
64
|
def _match(
|
|
58
65
|
self,
|
|
59
66
|
tool_name: str,
|
|
@@ -91,7 +98,12 @@ def collect_denied_agent_types(registry: PermissionRuleRegistry) -> frozenset[st
|
|
|
91
98
|
def _decision_from_rule(rule: PermissionRule, *, behavior: str) -> AuthorizationDecision:
|
|
92
99
|
rule_str = permission_rule_to_string(rule)
|
|
93
100
|
policy_id = f"permission_rule:{rule.source_name}:{rule_str}"
|
|
94
|
-
if behavior == "
|
|
101
|
+
if behavior == "allow":
|
|
102
|
+
decision = AuthorizationDecision(
|
|
103
|
+
behavior="allow",
|
|
104
|
+
policy_id=policy_id,
|
|
105
|
+
)
|
|
106
|
+
elif behavior == "ask":
|
|
95
107
|
decision = AuthorizationDecision(
|
|
96
108
|
behavior="ask",
|
|
97
109
|
ask_prompt=(
|
|
@@ -106,6 +106,30 @@ class PermissionRule:
|
|
|
106
106
|
return s.value if isinstance(s, PermissionRuleSource) else str(s)
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
def _content_rule_matches_target(
|
|
110
|
+
*,
|
|
111
|
+
tool_name: str,
|
|
112
|
+
target: str,
|
|
113
|
+
rule_content: str,
|
|
114
|
+
) -> bool:
|
|
115
|
+
if tool_name == "Bash":
|
|
116
|
+
from langchain_agentx.tools.bash.command_allow_rules import (
|
|
117
|
+
bash_command_matches_allow_rule,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return bash_command_matches_allow_rule(target, rule_content)
|
|
121
|
+
if tool_name == "WebFetch":
|
|
122
|
+
from langchain_agentx.tools.webfetch.registry_permission import (
|
|
123
|
+
webfetch_rule_content_matches,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return webfetch_rule_content_matches(
|
|
127
|
+
target=target,
|
|
128
|
+
rule_content=rule_content,
|
|
129
|
+
)
|
|
130
|
+
return fnmatch.fnmatch(target, rule_content)
|
|
131
|
+
|
|
132
|
+
|
|
109
133
|
def permission_rule_value_from_string(rule_string: str) -> PermissionRuleValue:
|
|
110
134
|
"""解析 CC 规则串,如 ``Bash(python:*)`` 或工具级 ``Read``。"""
|
|
111
135
|
text = rule_string.strip()
|
|
@@ -254,7 +278,7 @@ class PermissionRuleRegistry:
|
|
|
254
278
|
target: str,
|
|
255
279
|
behavior: PermissionRuleBehavior,
|
|
256
280
|
) -> PermissionRule | None:
|
|
257
|
-
"""CC Tool(content) 规则:工具名精确匹配 + fnmatch
|
|
281
|
+
"""CC Tool(content) 规则:工具名精确匹配 + fnmatch / Bash prefix:*。"""
|
|
258
282
|
tool_key = tool_name.strip()
|
|
259
283
|
if not tool_key:
|
|
260
284
|
return None
|
|
@@ -264,7 +288,11 @@ class PermissionRuleRegistry:
|
|
|
264
288
|
continue
|
|
265
289
|
if rule.tool_name != tool_key:
|
|
266
290
|
continue
|
|
267
|
-
if
|
|
291
|
+
if _content_rule_matches_target(
|
|
292
|
+
tool_name=tool_key,
|
|
293
|
+
target=target,
|
|
294
|
+
rule_content=content,
|
|
295
|
+
):
|
|
268
296
|
return rule
|
|
269
297
|
return None
|
|
270
298
|
|
|
@@ -274,6 +302,8 @@ class PermissionRuleRegistry:
|
|
|
274
302
|
behavior: PermissionRuleBehavior,
|
|
275
303
|
) -> str | None:
|
|
276
304
|
"""v1 deny_globs/ask_globs 减法:裸 glob / 命令模式 fnmatch。"""
|
|
305
|
+
if behavior is PermissionRuleBehavior.ALLOW:
|
|
306
|
+
return None
|
|
277
307
|
patterns = (
|
|
278
308
|
self.legacy_deny_globs
|
|
279
309
|
if behavior is PermissionRuleBehavior.DENY
|
|
@@ -25,12 +25,12 @@ MAX_PREVIEW_SNAPSHOT_CHARS: int = 8192
|
|
|
25
25
|
# present():每段 preview / snapshot 写入模型可见文本时的默认截断
|
|
26
26
|
PRESENT_PREVIEW_SNIPPET_MAX: int = 500
|
|
27
27
|
|
|
28
|
-
#
|
|
28
|
+
# 全角问号(中文输入法常见);仅作 normalize 软转换,不作为 validate 硬拒条件
|
|
29
29
|
FULLWIDTH_QUESTION_MARK: str = "\uff1f"
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def normalize_question_text(text: str) -> str:
|
|
33
|
-
"""将题干末尾全角 ``?`` 规范为 ASCII
|
|
33
|
+
"""将题干末尾全角 ``?`` 规范为 ASCII ``?``(展示一致性;校验不强制末尾问号)。"""
|
|
34
34
|
if text.endswith(FULLWIDTH_QUESTION_MARK):
|
|
35
35
|
return text[:-1] + "?"
|
|
36
36
|
return text
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
tools/ask_user_question/validators.py — QuestionValidator
|
|
3
3
|
|
|
4
4
|
职责:
|
|
5
|
-
|
|
5
|
+
封装单题与整卷语义校验(非空题干、选项数、label 唯一等)。
|
|
6
6
|
|
|
7
7
|
链路位置:
|
|
8
8
|
AskUserQuestionTool.validate_input 委托本类。
|
|
9
9
|
|
|
10
10
|
当前裁剪范围:
|
|
11
|
-
|
|
11
|
+
题干末尾问号仅为 schema/prompt 软提示(对齐 CC validateInput,无硬拒);
|
|
12
|
+
preview 长度与 Pydantic 双保险;HTML 规则在 HtmlPreviewValidator。
|
|
12
13
|
|
|
13
14
|
"""
|
|
14
15
|
|
|
@@ -32,11 +33,12 @@ class QuestionValidator:
|
|
|
32
33
|
code="MISSING_QUESTION_FIELD",
|
|
33
34
|
)
|
|
34
35
|
question_text = question["question"]
|
|
35
|
-
|
|
36
|
+
# CC: Zod describe 建议 end with '?';validateInput 不硬拒末尾字符。
|
|
37
|
+
if not isinstance(question_text, str) or not question_text.strip():
|
|
36
38
|
return ValidationResult(
|
|
37
39
|
ok=False,
|
|
38
|
-
message=f"Question at index {index}: '
|
|
39
|
-
code="
|
|
40
|
+
message=f"Question at index {index}: 'question' must be a non-empty string",
|
|
41
|
+
code="EMPTY_QUESTION_TEXT",
|
|
40
42
|
)
|
|
41
43
|
|
|
42
44
|
header = question.get("header", "")
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
"""
|
|
2
|
-
tools/bash/command_allow_rules.py —
|
|
2
|
+
tools/bash/command_allow_rules.py — Bash 内容级 allow 匹配(slash command + cliArg registry)。
|
|
3
3
|
|
|
4
4
|
职责:
|
|
5
|
-
|
|
5
|
+
CC 风格 Bash(git:*) / Bash(python:*) 前缀规则;slash 白名单与 registry cliArg 内容 allow。
|
|
6
6
|
|
|
7
7
|
链路位置:
|
|
8
|
-
BashRuntimeTool.
|
|
8
|
+
BashRuntimeTool._evaluate_bash_l1_chain / _authorize_command_segment。
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import fnmatch
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
14
15
|
|
|
15
16
|
from langchain_agentx.tool_runtime.models import AuthorizationDecision, ToolExecutionContext
|
|
16
17
|
from langchain_agentx.tool_runtime.permission_context import get_command_bash_allow_rules
|
|
18
|
+
from langchain_agentx.tool_runtime.permission_rule_matcher import RegistryPermissionMatcher
|
|
19
|
+
from langchain_agentx.tool_runtime.permission_rules import (
|
|
20
|
+
PermissionRuleBehavior,
|
|
21
|
+
PermissionRuleRegistry,
|
|
22
|
+
build_permission_rule_registry,
|
|
23
|
+
)
|
|
17
24
|
from langchain_agentx.tools.bash.read_only_validation import BashReadOnlyClassifier
|
|
18
25
|
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from langchain_agentx.tool_runtime.policy import DefaultPolicyEngine
|
|
28
|
+
|
|
19
29
|
|
|
20
30
|
def bash_command_matches_allow_rule(command: str, rule_content: str) -> bool:
|
|
21
31
|
"""CC prefix:* 与简单 fnmatch 子集。"""
|
|
@@ -55,6 +65,69 @@ def try_command_allow_decision(
|
|
|
55
65
|
return None
|
|
56
66
|
|
|
57
67
|
|
|
68
|
+
def cliarg_bash_has_content_allow_without_bare_tool(
|
|
69
|
+
registry: PermissionRuleRegistry,
|
|
70
|
+
) -> bool:
|
|
71
|
+
"""cliArg 仅配置了 Bash(pattern) 而无裸 Bash(CC-CLI-B-05 / B-08 边界)。"""
|
|
72
|
+
has_content = False
|
|
73
|
+
has_bare = False
|
|
74
|
+
for rule in registry.iter_rules(PermissionRuleBehavior.ALLOW):
|
|
75
|
+
if rule.source_name != "cliArg" or rule.tool_name != "Bash":
|
|
76
|
+
continue
|
|
77
|
+
if rule.rule_content is None:
|
|
78
|
+
has_bare = True
|
|
79
|
+
else:
|
|
80
|
+
has_content = True
|
|
81
|
+
return has_content and not has_bare
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def try_registry_bash_content_allow_decision(
|
|
85
|
+
command: str,
|
|
86
|
+
ctx: ToolExecutionContext,
|
|
87
|
+
policy_engine: DefaultPolicyEngine | None,
|
|
88
|
+
) -> AuthorizationDecision | None:
|
|
89
|
+
"""cliArg/settings 等内容级 Bash allow(CC bashPermissions prefix allow,path 通过后)。"""
|
|
90
|
+
if policy_engine is None:
|
|
91
|
+
return None
|
|
92
|
+
registry = build_permission_rule_registry(
|
|
93
|
+
policy_config=policy_engine._config,
|
|
94
|
+
session_store=ctx.session_store,
|
|
95
|
+
workspace_root=ctx.workspace_root,
|
|
96
|
+
agent_home_segment=ctx.agent_home_segment,
|
|
97
|
+
)
|
|
98
|
+
matcher = RegistryPermissionMatcher(registry)
|
|
99
|
+
return matcher.match_allow("Bash", command.strip())
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def promote_unmatched_cliarg_content_only_allow_to_ask(
|
|
103
|
+
decision: AuthorizationDecision,
|
|
104
|
+
*,
|
|
105
|
+
command: str,
|
|
106
|
+
ctx: ToolExecutionContext,
|
|
107
|
+
policy_engine: DefaultPolicyEngine | None,
|
|
108
|
+
) -> AuthorizationDecision:
|
|
109
|
+
"""roots 内 silent allow → ask(仅 cliArg 有 Bash(pattern) 且无裸 Bash 时)。"""
|
|
110
|
+
if policy_engine is None or decision.behavior != "allow" or decision.policy_id:
|
|
111
|
+
return decision
|
|
112
|
+
registry = build_permission_rule_registry(
|
|
113
|
+
policy_config=policy_engine._config,
|
|
114
|
+
session_store=ctx.session_store,
|
|
115
|
+
workspace_root=ctx.workspace_root,
|
|
116
|
+
agent_home_segment=ctx.agent_home_segment,
|
|
117
|
+
)
|
|
118
|
+
if not cliarg_bash_has_content_allow_without_bare_tool(registry):
|
|
119
|
+
return decision
|
|
120
|
+
return AuthorizationDecision(
|
|
121
|
+
behavior="ask",
|
|
122
|
+
message=decision.message,
|
|
123
|
+
policy_id="write_roots",
|
|
124
|
+
ask_prompt=(
|
|
125
|
+
decision.ask_prompt
|
|
126
|
+
or f"Allow Bash command in workspace?\n{command.strip()}"
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
58
131
|
_READ_ONLY_CLASSIFIER = BashReadOnlyClassifier()
|
|
59
132
|
|
|
60
133
|
|
|
@@ -42,7 +42,12 @@ from .limits import get_bash_limits
|
|
|
42
42
|
from .mode_validation import BashPermissionModeValidator
|
|
43
43
|
from .models import BashToolInput, BashToolOutput
|
|
44
44
|
from .boundary_check import authorize_bash_boundary
|
|
45
|
-
from .command_allow_rules import
|
|
45
|
+
from .command_allow_rules import (
|
|
46
|
+
promote_unmatched_cliarg_content_only_allow_to_ask,
|
|
47
|
+
try_command_allow_decision,
|
|
48
|
+
try_command_restrict_decision,
|
|
49
|
+
try_registry_bash_content_allow_decision,
|
|
50
|
+
)
|
|
46
51
|
from .events import (
|
|
47
52
|
BashProgressAccumulator,
|
|
48
53
|
bash_progress_emit_data,
|
|
@@ -552,8 +557,15 @@ class BashRuntimeTool(RuntimeTool):
|
|
|
552
557
|
(item[1].updated_input for item in decisions if item[1].updated_input),
|
|
553
558
|
None,
|
|
554
559
|
)
|
|
560
|
+
_, primary_allow = decisions[0]
|
|
555
561
|
return self._with_observability(
|
|
556
|
-
AuthorizationDecision(
|
|
562
|
+
AuthorizationDecision(
|
|
563
|
+
behavior="allow",
|
|
564
|
+
message=primary_allow.message,
|
|
565
|
+
policy_id=primary_allow.policy_id,
|
|
566
|
+
updated_input=updated_input or primary_allow.updated_input,
|
|
567
|
+
metadata=dict(primary_allow.metadata or {}),
|
|
568
|
+
),
|
|
557
569
|
trace=trace,
|
|
558
570
|
trace_context=TraceContext.from_ctx(ctx),
|
|
559
571
|
)
|
|
@@ -977,6 +989,12 @@ class BashRuntimeTool(RuntimeTool):
|
|
|
977
989
|
if path_decision.behavior != "allow":
|
|
978
990
|
return path_decision
|
|
979
991
|
|
|
992
|
+
registry_allow = try_registry_bash_content_allow_decision(
|
|
993
|
+
command, ctx, self._policy
|
|
994
|
+
)
|
|
995
|
+
if registry_allow is not None:
|
|
996
|
+
return registry_allow
|
|
997
|
+
|
|
980
998
|
hardening_decision = self._security_hardener.check_segment(
|
|
981
999
|
command=command,
|
|
982
1000
|
analysis=analysis,
|
|
@@ -1015,7 +1033,13 @@ class BashRuntimeTool(RuntimeTool):
|
|
|
1015
1033
|
if decision.behavior != "allow":
|
|
1016
1034
|
return decision
|
|
1017
1035
|
|
|
1018
|
-
|
|
1036
|
+
final = self._authorize_sed_segment(command=command, ctx=ctx, cwd=cwd) or decision
|
|
1037
|
+
return promote_unmatched_cliarg_content_only_allow_to_ask(
|
|
1038
|
+
final,
|
|
1039
|
+
command=command,
|
|
1040
|
+
ctx=ctx,
|
|
1041
|
+
policy_engine=self._policy,
|
|
1042
|
+
)
|
|
1019
1043
|
|
|
1020
1044
|
def _authorize_sed_segment(
|
|
1021
1045
|
self,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/webfetch/registry_permission.py — WebFetch registry 内容规则(CC domain:hostname)
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
将 URL 转为 CC ``webFetchToolInputToPermissionRuleContent`` 键;在 L1 按 deny→ask→allow 评估。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
WebFetchRuntimeTool.run_l1_permission_check(预批准 host 之后)。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
14
|
+
from urllib.parse import urlparse
|
|
15
|
+
|
|
16
|
+
from langchain_agentx.tool_runtime.models import AuthorizationDecision
|
|
17
|
+
from langchain_agentx.tool_runtime.permission_decision import (
|
|
18
|
+
attach_decision_reason,
|
|
19
|
+
make_other_decision_reason,
|
|
20
|
+
)
|
|
21
|
+
from langchain_agentx.tool_runtime.permission_rule_matcher import RegistryPermissionMatcher
|
|
22
|
+
from langchain_agentx.tool_runtime.permission_rules import build_permission_rule_registry
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from langchain_agentx.tool_runtime.models import ToolExecutionContext
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def webfetch_permission_rule_content(url: str) -> str:
|
|
29
|
+
"""对齐 CC WebFetchTool.ts ``domain:${hostname}``。"""
|
|
30
|
+
text = url.strip()
|
|
31
|
+
if not text:
|
|
32
|
+
return "input:"
|
|
33
|
+
try:
|
|
34
|
+
host = (urlparse(text).hostname or "").lower()
|
|
35
|
+
if host:
|
|
36
|
+
return f"domain:{host}"
|
|
37
|
+
except Exception:
|
|
38
|
+
pass
|
|
39
|
+
return f"input:{text}"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def webfetch_rule_content_matches(*, target: str, rule_content: str) -> bool:
|
|
43
|
+
"""WebFetch 内容规则:target 与 rule 均为 ``domain:host`` 或 ``input:…``。"""
|
|
44
|
+
return target.strip().lower() == rule_content.strip().lower()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def evaluate_webfetch_registry_permission(
|
|
48
|
+
url: str,
|
|
49
|
+
ctx: ToolExecutionContext,
|
|
50
|
+
policy_engine: Any,
|
|
51
|
+
) -> AuthorizationDecision:
|
|
52
|
+
registry = build_permission_rule_registry(
|
|
53
|
+
policy_config=policy_engine._config,
|
|
54
|
+
session_store=ctx.session_store,
|
|
55
|
+
workspace_root=ctx.workspace_root,
|
|
56
|
+
agent_home_segment=ctx.agent_home_segment,
|
|
57
|
+
)
|
|
58
|
+
target = webfetch_permission_rule_content(url)
|
|
59
|
+
matcher = RegistryPermissionMatcher(registry)
|
|
60
|
+
for match_fn in (matcher.match_deny, matcher.match_ask, matcher.match_allow):
|
|
61
|
+
matched = match_fn("WebFetch", target)
|
|
62
|
+
if matched is not None:
|
|
63
|
+
return matched
|
|
64
|
+
decision = AuthorizationDecision(
|
|
65
|
+
behavior="ask",
|
|
66
|
+
ask_prompt=(
|
|
67
|
+
"Claude requested permissions to use WebFetch, but you haven't granted it yet."
|
|
68
|
+
),
|
|
69
|
+
policy_id="webfetch_default",
|
|
70
|
+
)
|
|
71
|
+
return attach_decision_reason(
|
|
72
|
+
decision,
|
|
73
|
+
make_other_decision_reason("WebFetch default permission prompt"),
|
|
74
|
+
)
|
|
@@ -123,7 +123,18 @@ class WebFetchRuntimeTool(RuntimeTool):
|
|
|
123
123
|
outcome="allow",
|
|
124
124
|
decision=AuthorizationDecision(behavior="allow"),
|
|
125
125
|
)
|
|
126
|
-
|
|
126
|
+
if self._policy is None:
|
|
127
|
+
from langchain_agentx.tool_runtime.policy import DefaultPolicyEngine, ToolPolicyConfig
|
|
128
|
+
|
|
129
|
+
engine = DefaultPolicyEngine(ToolPolicyConfig())
|
|
130
|
+
else:
|
|
131
|
+
engine = self._policy
|
|
132
|
+
from langchain_agentx.tools.webfetch.registry_permission import (
|
|
133
|
+
evaluate_webfetch_registry_permission,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
decision = evaluate_webfetch_registry_permission(url, ctx, engine)
|
|
137
|
+
return L1PermissionResult(outcome=decision.behavior, decision=decision)
|
|
127
138
|
|
|
128
139
|
def invoke(self, data: dict[str, Any], ctx: ToolExecutionContext) -> WebFetchToolOutput:
|
|
129
140
|
parsed = _as_input(data)
|
|
@@ -155,7 +155,23 @@ class WebSearchRuntimeTool(RuntimeTool):
|
|
|
155
155
|
outcome="deny",
|
|
156
156
|
decision=AuthorizationDecision(behavior="deny", message=deny_reason),
|
|
157
157
|
)
|
|
158
|
-
|
|
158
|
+
from langchain_agentx.tool_runtime.permission_decision import (
|
|
159
|
+
attach_decision_reason,
|
|
160
|
+
make_other_decision_reason,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
decision = AuthorizationDecision(
|
|
164
|
+
behavior="ask",
|
|
165
|
+
ask_prompt="WebSearch requires permission.",
|
|
166
|
+
policy_id="websearch_default",
|
|
167
|
+
)
|
|
168
|
+
return L1PermissionResult(
|
|
169
|
+
outcome="ask",
|
|
170
|
+
decision=attach_decision_reason(
|
|
171
|
+
decision,
|
|
172
|
+
make_other_decision_reason("WebSearch passthrough promoted to ask"),
|
|
173
|
+
),
|
|
174
|
+
)
|
|
159
175
|
|
|
160
176
|
def invoke(self, data: dict[str, Any], ctx: ToolExecutionContext) -> WebSearchToolOutput:
|
|
161
177
|
parsed = WebSearchToolInput.model_validate(data)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
langchain_agentx/__init__.py,sha256=
|
|
1
|
+
langchain_agentx/__init__.py,sha256=sLs2vKbNQCEmhW-uC3NdfDAK1zHUjcNzPxy1y2UPlqg,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
|
|
@@ -389,7 +389,7 @@ langchain_agentx/task_runtime/tasks/trace_cleanup/__init__.py,sha256=riWlkXNpJb3
|
|
|
389
389
|
langchain_agentx/task_runtime/tasks/trace_cleanup/bootstrap.py,sha256=xYVabnY0Qe4AnKKXhLIL10h22ZOwtaxRGdQ9ew0T9zo,3399
|
|
390
390
|
langchain_agentx/task_runtime/tasks/trace_cleanup/executor.py,sha256=qez3WnzBTagYZMc0qA1mk-jvM2dMy7uCrwZ7aWhEJzw,2094
|
|
391
391
|
langchain_agentx/task_runtime/tasks/trace_cleanup/scheduler.py,sha256=lqWlCqucs26fhr4M9LfgY8I0xuNDxwSLkre6XjAJDhA,6877
|
|
392
|
-
langchain_agentx/tool_runtime/__init__.py,sha256=
|
|
392
|
+
langchain_agentx/tool_runtime/__init__.py,sha256=bl_PIAbVXuaPgM-qH4z0mh7vgKBSttoxb-XMxsshles,5297
|
|
393
393
|
langchain_agentx/tool_runtime/accept_edits_fast_path.py,sha256=UMCOF9Ws7hx3pcYvL-Kq7vzIAGvOGdEfx69ccUDXT-k,2281
|
|
394
394
|
langchain_agentx/tool_runtime/adapter.py,sha256=rM-Y2zSZ--N-TIvx1HtI1J7ralPU0w4jJuX3bBvIVxM,33847
|
|
395
395
|
langchain_agentx/tool_runtime/agent_home_bypass.py,sha256=na9xQPgJ02qA3YiJ1F8jN0Uv4uBDPzqRk3lfPPE089g,8010
|
|
@@ -404,7 +404,7 @@ 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=SSr45DuwiuKxoyb3r7DdrRu7YUFzKIWBCmTYhBKFO68,17594
|
|
408
408
|
langchain_agentx/tool_runtime/limits.py,sha256=imE1IetmyKQM82INR0VkP3qZNM4bjUJuf1CG5BkY0uw,356
|
|
409
409
|
langchain_agentx/tool_runtime/loader.py,sha256=ZeT-xIB6TDkehqyYWx1gkvI0aKx9_xKDKJhqyIvlAvw,9397
|
|
410
410
|
langchain_agentx/tool_runtime/loop_loader.py,sha256=C_4T9XxlDmKaTzGv4MO6mJuR-rd6uueb1ZWwEKvvFaE,9087
|
|
@@ -414,11 +414,12 @@ langchain_agentx/tool_runtime/models.py,sha256=cO9EgLKPXiktHl_xXtBwLQrCo6mj4tbT4
|
|
|
414
414
|
langchain_agentx/tool_runtime/outer_permission_resolver.py,sha256=8uKS148DXxt5SfDIHmbRwee4FchKdlquUZbfWeyNRXw,4184
|
|
415
415
|
langchain_agentx/tool_runtime/override.py,sha256=D8TRftoeWzqlTuWCGUxNkFY7XzWx_d5oxI_ltNibqPA,10258
|
|
416
416
|
langchain_agentx/tool_runtime/path_safety.py,sha256=dUrl2SObKmRDwdm9k8veO_Eh0iXw0LPLAMZJGxeKnTE,8548
|
|
417
|
+
langchain_agentx/tool_runtime/permission_cli_rules.py,sha256=fz8cW-jXH3f92EYfO0ShIaBSC0k2Lb6GtLJwCpLiaPk,4799
|
|
417
418
|
langchain_agentx/tool_runtime/permission_context.py,sha256=ReVV2kjVWAPTsPaYXxRB-dICqualEvi2zzwF8NX_Wkk,6229
|
|
418
419
|
langchain_agentx/tool_runtime/permission_decision.py,sha256=-dWuC60MV6PYgHHOsSxr6Qad8Mm3qgMuh6nkzM87LO4,10280
|
|
419
420
|
langchain_agentx/tool_runtime/permission_orchestrator.py,sha256=nwx76k74aAFgo63tfkoi6zUJTj9-10tnQzj03VYqFuw,4637
|
|
420
|
-
langchain_agentx/tool_runtime/permission_rule_matcher.py,sha256=
|
|
421
|
-
langchain_agentx/tool_runtime/permission_rules.py,sha256=
|
|
421
|
+
langchain_agentx/tool_runtime/permission_rule_matcher.py,sha256=z4VesjyvvEry-L2U1VsrJB3SR6wtaG8OOuhvftsvj9U,4725
|
|
422
|
+
langchain_agentx/tool_runtime/permission_rules.py,sha256=Aw4qTckMCpQDZpGeAHkRS4fON3ixWU9B5B9W6D2OlMg,19435
|
|
422
423
|
langchain_agentx/tool_runtime/permission_settings_loader.py,sha256=Xu6dso0ghh4pb7Puf8nj20nXxjGeQvL4kIGycavwG_o,5624
|
|
423
424
|
langchain_agentx/tool_runtime/permission_settings_sync.py,sha256=g6TvjWwa7D0kw5rDznPsoMv7xylESPOyWA2vZW4HDm0,4590
|
|
424
425
|
langchain_agentx/tool_runtime/permission_snapshot.py,sha256=xFoi9fIcZjSw_zrIoyglneM-vJsDOLCEjiVoazr5Ack,1947
|
|
@@ -520,12 +521,12 @@ langchain_agentx/tools/agent/registry/__init__.py,sha256=Ts5_OJzTnbiqZDxXqMI1UnF
|
|
|
520
521
|
langchain_agentx/tools/agent/registry/config.py,sha256=4bcI0Gq1Kqclc3y6YsDhVSkc8tNTkslQAPqy5ZSgF-Y,1628
|
|
521
522
|
langchain_agentx/tools/agent/registry/registry.py,sha256=knii6XWFZvPkcqzTK3Y4PX5evGcDmto4SsZ7VzUsPSI,1544
|
|
522
523
|
langchain_agentx/tools/ask_user_question/__init__.py,sha256=bDHVPc4dHBw___7rbaTxClsbDpQ-jLt5Obj8R2VBLAc,444
|
|
523
|
-
langchain_agentx/tools/ask_user_question/constants.py,sha256=
|
|
524
|
+
langchain_agentx/tools/ask_user_question/constants.py,sha256=rqlL31BTSNY2spn8f2i-MtKjy3y3QoLFAeqenFmYe6Q,1283
|
|
524
525
|
langchain_agentx/tools/ask_user_question/html_preview.py,sha256=OwNpsH9qm8iJ-TTbzkMFvG49T4-oNJXJXH4OZ8r55wc,3913
|
|
525
526
|
langchain_agentx/tools/ask_user_question/models.py,sha256=uLc2UK_v8iMFEL1Zk0mUOBYdcg2HfbaLdH68o8kr7GE,3376
|
|
526
527
|
langchain_agentx/tools/ask_user_question/prompt.py,sha256=0RdVuUeNzQBzPeVrLcbiBqh1uizFgUANvaKOZpVy_qg,2803
|
|
527
528
|
langchain_agentx/tools/ask_user_question/tool.py,sha256=DgU7OBAqrJBb77ewQ76okoUFJxTThAiN6PRMPa8_FvU,13467
|
|
528
|
-
langchain_agentx/tools/ask_user_question/validators.py,sha256=
|
|
529
|
+
langchain_agentx/tools/ask_user_question/validators.py,sha256=Uc88cBzqUp0PNG_Jf2yX2WtKP8hzBsnkMb_KUXh9C4M,6088
|
|
529
530
|
langchain_agentx/tools/bash/__init__.py,sha256=molPHFDylE2wWG_aQx7fb0ciAx3wWpdXzIixTZV5ne4,214
|
|
530
531
|
langchain_agentx/tools/bash/ast_security.py,sha256=6DFd_TwT986dbGkKT3UnDIZioBdmi8mbJcYSI-XEtdM,22444
|
|
531
532
|
langchain_agentx/tools/bash/auto_mode_adapter.py,sha256=7-yb_kGmpytBYkD2Hn5YWPIceo_YN4PatEKj8O7HKsM,4243
|
|
@@ -533,7 +534,7 @@ langchain_agentx/tools/bash/backend.py,sha256=E0DMBAENZLUSLvuEh6RubNJ6iCVnx1d2VG
|
|
|
533
534
|
langchain_agentx/tools/bash/bash_hardening.py,sha256=KEYKRC7kS_to4mzXuChvCmOIIb4dSUkOb9K1MBDtWDc,29296
|
|
534
535
|
langchain_agentx/tools/bash/bash_runtime_contract.py,sha256=s8vrvMl7405KLGw2hPFbzlzWohm5JX3c9cImMBgqh8I,1511
|
|
535
536
|
langchain_agentx/tools/bash/boundary_check.py,sha256=k9Bv-baIAUDE3-c_Brw3VSXXYMv_g3lTM0mabIadnwc,8822
|
|
536
|
-
langchain_agentx/tools/bash/command_allow_rules.py,sha256
|
|
537
|
+
langchain_agentx/tools/bash/command_allow_rules.py,sha256=-1m-yzTCEBHnomeliejUs-CR9yBr9SqtczKoLZIZKsc,5950
|
|
537
538
|
langchain_agentx/tools/bash/cwd_reporter.py,sha256=mASbKWLb4MiX4zLKY3w1ocIdfhmwj5KRgX4OW_PcAns,2642
|
|
538
539
|
langchain_agentx/tools/bash/events.py,sha256=xDpPA3zhne2cnlYTmiiPTsV423-h-Nw-VKDR_rlnbGE,6065
|
|
539
540
|
langchain_agentx/tools/bash/l1_decision_reason.py,sha256=MOL9HBBpJEgypuTiYdNojQBl8eIsTwRge5CtGeYRJKI,5365
|
|
@@ -557,7 +558,7 @@ langchain_agentx/tools/bash/semantics.py,sha256=pkf4URTulAOVL7m04Rq9JJF7z0J2mTEs
|
|
|
557
558
|
langchain_agentx/tools/bash/shell_locator.py,sha256=AUnCZS8gmZurCs8rL4y0MMOS9UZimfg5rQ5At38mZKU,6296
|
|
558
559
|
langchain_agentx/tools/bash/shell_quoting.py,sha256=K4JLVGwU8VpdQzfQB1o3xsq4HoCKHew1pry8hHChcoA,4498
|
|
559
560
|
langchain_agentx/tools/bash/task_runtime.py,sha256=hikVC9YugtvpLf4nDaPlX_GFem1YQvDRwN0ShRflUYU,3568
|
|
560
|
-
langchain_agentx/tools/bash/tool.py,sha256=
|
|
561
|
+
langchain_agentx/tools/bash/tool.py,sha256=xhuFynhBg4Vcar3WjLoJptlYPoqRnkFiV_lQRi0l6T0,46387
|
|
561
562
|
langchain_agentx/tools/bash/windows_shell_quoting.py,sha256=0RzLnTKyFrJ536QzqPZvSyUcQWN794XRlAq2RdHeKHo,1615
|
|
562
563
|
langchain_agentx/tools/edit/__init__.py,sha256=PiiOHUODUzTRQxxPdE7ZMTjd3vfdzm9LEcn1lpVtg8o,203
|
|
563
564
|
langchain_agentx/tools/edit/backend.py,sha256=JrikYafZDGMQ2JjXRxbnv1VSHVGRUdLmZN__nnqkM9o,3159
|
|
@@ -649,8 +650,9 @@ langchain_agentx/tools/webfetch/loader.py,sha256=QCATTqBvQHXiMVuAbGQGQeymVevDfWw
|
|
|
649
650
|
langchain_agentx/tools/webfetch/models.py,sha256=e_k3FS_9JLekKsq1auRioLDhUFz4YNUlX3MrvnW5BE0,2561
|
|
650
651
|
langchain_agentx/tools/webfetch/preapproved.py,sha256=8SjbOZdZla208oml8pzixkA-vvZ9c0kofCQ-3XQCoeQ,4312
|
|
651
652
|
langchain_agentx/tools/webfetch/prompt.py,sha256=lpiAV9ftjs-M1BS6R5_L_uNAcOPJ5bJ9H-ZE2eq_5OY,3771
|
|
653
|
+
langchain_agentx/tools/webfetch/registry_permission.py,sha256=5jSg_c2TuAqNGMA1_C_pSNRufN2fuOaqTOEcitjaQf8,2535
|
|
652
654
|
langchain_agentx/tools/webfetch/summary.py,sha256=wk-5C39VoEIj9OyZtw6DOs6GYtdvPVECb80uGgVwI7w,1038
|
|
653
|
-
langchain_agentx/tools/webfetch/tool.py,sha256=
|
|
655
|
+
langchain_agentx/tools/webfetch/tool.py,sha256=4qT0nH2DwC8iPEWzmnZjns8lnynXS7muc-2xCgVlbZY,9199
|
|
654
656
|
langchain_agentx/tools/websearch/__init__.py,sha256=gOVwLv3X9iLUpnN5lhyLeEiovw-JDmzdqxGg7ufkIMY,950
|
|
655
657
|
langchain_agentx/tools/websearch/backend.py,sha256=AIv9vh1rwFLlYmSEl-NRTM_LiMHRu5sMiMpE5CksWrg,8871
|
|
656
658
|
langchain_agentx/tools/websearch/events.py,sha256=iQY_vEdCS1mCYfiB7jFMrGNUbpnVI9gJoA6jaXvNxeA,810
|
|
@@ -658,7 +660,7 @@ langchain_agentx/tools/websearch/limits.py,sha256=trZxxFAXHUv2JstYxMIJb7_hIWYkXm
|
|
|
658
660
|
langchain_agentx/tools/websearch/loader.py,sha256=Y9FX3BPGe_oBSpqfzDke7XN4QqRjjVSVprnsSd0K_SI,1489
|
|
659
661
|
langchain_agentx/tools/websearch/models.py,sha256=YkVhxM2CXj2RAn4KtOO_dgxZuvo4viJ0AUdAy06-4hM,4167
|
|
660
662
|
langchain_agentx/tools/websearch/prompt.py,sha256=Sy2zlAO233WvmpOkJuCrkTCpS6oZIkCS_i2CmhBb_js,3188
|
|
661
|
-
langchain_agentx/tools/websearch/tool.py,sha256=
|
|
663
|
+
langchain_agentx/tools/websearch/tool.py,sha256=KQW_awFJqcSVV-oltjb4QTQGjLqC8RnTCZtgbVXCIZ0,9406
|
|
662
664
|
langchain_agentx/tools/write/__init__.py,sha256=lsmRlcXRgiN3boyfp3QjqRdhHarl2xJkC_XjN5AvEFk,171
|
|
663
665
|
langchain_agentx/tools/write/backend.py,sha256=qZ86LKIunfIYLsfVZrwvBiJsjmvJCbNA_PB4ZLUuHFM,2298
|
|
664
666
|
langchain_agentx/tools/write/display_builder.py,sha256=3pRUuLyOoHzY9iqHMeg8yTVCby5AAFz2Zk3NMOttEbM,3932
|
|
@@ -761,8 +763,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
|
|
|
761
763
|
langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
|
|
762
764
|
langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
|
|
763
765
|
langchain_agentx/workspace/view.py,sha256=PGasqTaqhlD03SXXazHuw4RHfV681AIdlsYqL70pEjc,4774
|
|
764
|
-
langchain_agentx_python-2.2.
|
|
765
|
-
langchain_agentx_python-2.2.
|
|
766
|
-
langchain_agentx_python-2.2.
|
|
767
|
-
langchain_agentx_python-2.2.
|
|
768
|
-
langchain_agentx_python-2.2.
|
|
766
|
+
langchain_agentx_python-2.2.9.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
767
|
+
langchain_agentx_python-2.2.9.dist-info/METADATA,sha256=MWx8nrJ8lXDQorwBf7tximEwOMScMgBTbNOqshUd2v0,24250
|
|
768
|
+
langchain_agentx_python-2.2.9.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
|
|
769
|
+
langchain_agentx_python-2.2.9.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
|
|
770
|
+
langchain_agentx_python-2.2.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{langchain_agentx_python-2.2.7.dist-info → langchain_agentx_python-2.2.9.dist-info}/top_level.txt
RENAMED
|
File without changes
|