nc1709 1.15.4__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.
- nc1709/__init__.py +13 -0
- nc1709/agent/__init__.py +36 -0
- nc1709/agent/core.py +505 -0
- nc1709/agent/mcp_bridge.py +245 -0
- nc1709/agent/permissions.py +298 -0
- nc1709/agent/tools/__init__.py +21 -0
- nc1709/agent/tools/base.py +440 -0
- nc1709/agent/tools/bash_tool.py +367 -0
- nc1709/agent/tools/file_tools.py +454 -0
- nc1709/agent/tools/notebook_tools.py +516 -0
- nc1709/agent/tools/search_tools.py +322 -0
- nc1709/agent/tools/task_tool.py +284 -0
- nc1709/agent/tools/web_tools.py +555 -0
- nc1709/agents/__init__.py +17 -0
- nc1709/agents/auto_fix.py +506 -0
- nc1709/agents/test_generator.py +507 -0
- nc1709/checkpoints.py +372 -0
- nc1709/cli.py +3380 -0
- nc1709/cli_ui.py +1080 -0
- nc1709/cognitive/__init__.py +149 -0
- nc1709/cognitive/anticipation.py +594 -0
- nc1709/cognitive/context_engine.py +1046 -0
- nc1709/cognitive/council.py +824 -0
- nc1709/cognitive/learning.py +761 -0
- nc1709/cognitive/router.py +583 -0
- nc1709/cognitive/system.py +519 -0
- nc1709/config.py +155 -0
- nc1709/custom_commands.py +300 -0
- nc1709/executor.py +333 -0
- nc1709/file_controller.py +354 -0
- nc1709/git_integration.py +308 -0
- nc1709/github_integration.py +477 -0
- nc1709/image_input.py +446 -0
- nc1709/linting.py +519 -0
- nc1709/llm_adapter.py +667 -0
- nc1709/logger.py +192 -0
- nc1709/mcp/__init__.py +18 -0
- nc1709/mcp/client.py +370 -0
- nc1709/mcp/manager.py +407 -0
- nc1709/mcp/protocol.py +210 -0
- nc1709/mcp/server.py +473 -0
- nc1709/memory/__init__.py +20 -0
- nc1709/memory/embeddings.py +325 -0
- nc1709/memory/indexer.py +474 -0
- nc1709/memory/sessions.py +432 -0
- nc1709/memory/vector_store.py +451 -0
- nc1709/models/__init__.py +86 -0
- nc1709/models/detector.py +377 -0
- nc1709/models/formats.py +315 -0
- nc1709/models/manager.py +438 -0
- nc1709/models/registry.py +497 -0
- nc1709/performance/__init__.py +343 -0
- nc1709/performance/cache.py +705 -0
- nc1709/performance/pipeline.py +611 -0
- nc1709/performance/tiering.py +543 -0
- nc1709/plan_mode.py +362 -0
- nc1709/plugins/__init__.py +17 -0
- nc1709/plugins/agents/__init__.py +18 -0
- nc1709/plugins/agents/django_agent.py +912 -0
- nc1709/plugins/agents/docker_agent.py +623 -0
- nc1709/plugins/agents/fastapi_agent.py +887 -0
- nc1709/plugins/agents/git_agent.py +731 -0
- nc1709/plugins/agents/nextjs_agent.py +867 -0
- nc1709/plugins/base.py +359 -0
- nc1709/plugins/manager.py +411 -0
- nc1709/plugins/registry.py +337 -0
- nc1709/progress.py +443 -0
- nc1709/prompts/__init__.py +22 -0
- nc1709/prompts/agent_system.py +180 -0
- nc1709/prompts/task_prompts.py +340 -0
- nc1709/prompts/unified_prompt.py +133 -0
- nc1709/reasoning_engine.py +541 -0
- nc1709/remote_client.py +266 -0
- nc1709/shell_completions.py +349 -0
- nc1709/slash_commands.py +649 -0
- nc1709/task_classifier.py +408 -0
- nc1709/version_check.py +177 -0
- nc1709/web/__init__.py +8 -0
- nc1709/web/server.py +950 -0
- nc1709/web/templates/index.html +1127 -0
- nc1709-1.15.4.dist-info/METADATA +858 -0
- nc1709-1.15.4.dist-info/RECORD +86 -0
- nc1709-1.15.4.dist-info/WHEEL +5 -0
- nc1709-1.15.4.dist-info/entry_points.txt +2 -0
- nc1709-1.15.4.dist-info/licenses/LICENSE +9 -0
- nc1709-1.15.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Bridge
|
|
3
|
+
|
|
4
|
+
Integrates MCP (Model Context Protocol) servers into the agent's tool registry.
|
|
5
|
+
Allows the agent to use tools from external MCP servers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from .tools.base import Tool, ToolResult, ToolParameter, ToolPermission, ToolRegistry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MCPTool(Tool):
|
|
15
|
+
"""
|
|
16
|
+
A tool that wraps an MCP server tool.
|
|
17
|
+
|
|
18
|
+
Allows MCP tools to be used seamlessly alongside built-in tools.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
category = "mcp"
|
|
22
|
+
permission = ToolPermission.ASK # Ask before using MCP tools
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
name: str,
|
|
27
|
+
description: str,
|
|
28
|
+
parameters: List[ToolParameter],
|
|
29
|
+
mcp_manager,
|
|
30
|
+
server_name: str,
|
|
31
|
+
):
|
|
32
|
+
"""Initialize MCP tool wrapper
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
name: Tool name
|
|
36
|
+
description: Tool description
|
|
37
|
+
parameters: Tool parameters
|
|
38
|
+
mcp_manager: MCP manager for executing calls
|
|
39
|
+
server_name: Name of the MCP server providing this tool
|
|
40
|
+
"""
|
|
41
|
+
super().__init__()
|
|
42
|
+
self.name = name
|
|
43
|
+
self.description = description
|
|
44
|
+
self.parameters = parameters
|
|
45
|
+
self._mcp_manager = mcp_manager
|
|
46
|
+
self._server_name = server_name
|
|
47
|
+
|
|
48
|
+
def execute(self, **kwargs) -> ToolResult:
|
|
49
|
+
"""Execute the MCP tool"""
|
|
50
|
+
try:
|
|
51
|
+
# Run async MCP call synchronously
|
|
52
|
+
loop = asyncio.new_event_loop()
|
|
53
|
+
asyncio.set_event_loop(loop)
|
|
54
|
+
try:
|
|
55
|
+
result = loop.run_until_complete(
|
|
56
|
+
self._mcp_manager.call_tool(self.name, kwargs)
|
|
57
|
+
)
|
|
58
|
+
finally:
|
|
59
|
+
loop.close()
|
|
60
|
+
|
|
61
|
+
# Parse MCP result
|
|
62
|
+
if "error" in result:
|
|
63
|
+
return ToolResult(
|
|
64
|
+
success=False,
|
|
65
|
+
output="",
|
|
66
|
+
error=result["error"],
|
|
67
|
+
target=str(kwargs)[:30],
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Extract content
|
|
71
|
+
output = ""
|
|
72
|
+
if "content" in result:
|
|
73
|
+
for item in result["content"]:
|
|
74
|
+
if item.get("type") == "text":
|
|
75
|
+
output += item.get("text", "")
|
|
76
|
+
|
|
77
|
+
return ToolResult(
|
|
78
|
+
success=True,
|
|
79
|
+
output=output or str(result),
|
|
80
|
+
target=str(kwargs)[:30],
|
|
81
|
+
data=result,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
return ToolResult(
|
|
86
|
+
success=False,
|
|
87
|
+
output="",
|
|
88
|
+
error=f"MCP tool error: {e}",
|
|
89
|
+
target=str(kwargs)[:30],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class MCPBridge:
|
|
94
|
+
"""
|
|
95
|
+
Bridge between MCP servers and the agent tool registry.
|
|
96
|
+
|
|
97
|
+
Discovers tools from MCP servers and registers them as agent tools.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
def __init__(self, registry: ToolRegistry = None):
|
|
101
|
+
"""Initialize the MCP bridge
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
registry: Tool registry to add MCP tools to
|
|
105
|
+
"""
|
|
106
|
+
self.registry = registry or ToolRegistry()
|
|
107
|
+
self._mcp_manager = None
|
|
108
|
+
self._registered_tools: Dict[str, MCPTool] = {}
|
|
109
|
+
|
|
110
|
+
def connect_mcp_manager(self, mcp_manager) -> None:
|
|
111
|
+
"""Connect to an MCP manager
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
mcp_manager: MCPManager instance
|
|
115
|
+
"""
|
|
116
|
+
self._mcp_manager = mcp_manager
|
|
117
|
+
|
|
118
|
+
def discover_and_register_tools(self) -> int:
|
|
119
|
+
"""Discover tools from MCP servers and register them
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Number of tools registered
|
|
123
|
+
"""
|
|
124
|
+
if not self._mcp_manager:
|
|
125
|
+
return 0
|
|
126
|
+
|
|
127
|
+
count = 0
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
# Get all tools from MCP manager
|
|
131
|
+
all_tools = self._mcp_manager.get_all_tools()
|
|
132
|
+
|
|
133
|
+
# Register local tools
|
|
134
|
+
for tool in all_tools.get("local", []):
|
|
135
|
+
mcp_tool = self._create_mcp_tool(tool, "local")
|
|
136
|
+
if mcp_tool:
|
|
137
|
+
self.registry.register(mcp_tool)
|
|
138
|
+
self._registered_tools[mcp_tool.name] = mcp_tool
|
|
139
|
+
count += 1
|
|
140
|
+
|
|
141
|
+
# Register remote tools
|
|
142
|
+
for tool in all_tools.get("remote", []):
|
|
143
|
+
mcp_tool = self._create_mcp_tool(tool, "remote")
|
|
144
|
+
if mcp_tool:
|
|
145
|
+
self.registry.register(mcp_tool)
|
|
146
|
+
self._registered_tools[mcp_tool.name] = mcp_tool
|
|
147
|
+
count += 1
|
|
148
|
+
|
|
149
|
+
except Exception as e:
|
|
150
|
+
print(f"Error discovering MCP tools: {e}")
|
|
151
|
+
|
|
152
|
+
return count
|
|
153
|
+
|
|
154
|
+
def _create_mcp_tool(self, mcp_tool_def, server_name: str) -> Optional[MCPTool]:
|
|
155
|
+
"""Create an MCPTool from an MCP tool definition
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
mcp_tool_def: MCP tool definition object
|
|
159
|
+
server_name: Name of providing server
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
MCPTool instance or None
|
|
163
|
+
"""
|
|
164
|
+
try:
|
|
165
|
+
# Extract tool info
|
|
166
|
+
name = mcp_tool_def.name
|
|
167
|
+
description = mcp_tool_def.description or f"MCP tool: {name}"
|
|
168
|
+
|
|
169
|
+
# Convert parameters
|
|
170
|
+
parameters = []
|
|
171
|
+
if hasattr(mcp_tool_def, 'parameters') and mcp_tool_def.parameters:
|
|
172
|
+
for param in mcp_tool_def.parameters:
|
|
173
|
+
parameters.append(ToolParameter(
|
|
174
|
+
name=param.name,
|
|
175
|
+
description=param.description or param.name,
|
|
176
|
+
type=self._map_type(param.type) if hasattr(param, 'type') else "string",
|
|
177
|
+
required=getattr(param, 'required', True),
|
|
178
|
+
))
|
|
179
|
+
|
|
180
|
+
# Prefix MCP tool names to avoid conflicts
|
|
181
|
+
prefixed_name = f"mcp_{name}"
|
|
182
|
+
|
|
183
|
+
return MCPTool(
|
|
184
|
+
name=prefixed_name,
|
|
185
|
+
description=description,
|
|
186
|
+
parameters=parameters,
|
|
187
|
+
mcp_manager=self._mcp_manager,
|
|
188
|
+
server_name=server_name,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
except Exception as e:
|
|
192
|
+
print(f"Error creating MCP tool: {e}")
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
def _map_type(self, mcp_type: str) -> str:
|
|
196
|
+
"""Map MCP type to tool parameter type"""
|
|
197
|
+
type_map = {
|
|
198
|
+
"string": "string",
|
|
199
|
+
"str": "string",
|
|
200
|
+
"int": "integer",
|
|
201
|
+
"integer": "integer",
|
|
202
|
+
"float": "number",
|
|
203
|
+
"number": "number",
|
|
204
|
+
"bool": "boolean",
|
|
205
|
+
"boolean": "boolean",
|
|
206
|
+
"list": "array",
|
|
207
|
+
"array": "array",
|
|
208
|
+
"dict": "object",
|
|
209
|
+
"object": "object",
|
|
210
|
+
}
|
|
211
|
+
return type_map.get(mcp_type.lower(), "string")
|
|
212
|
+
|
|
213
|
+
def get_registered_tools(self) -> List[str]:
|
|
214
|
+
"""Get list of registered MCP tool names"""
|
|
215
|
+
return list(self._registered_tools.keys())
|
|
216
|
+
|
|
217
|
+
def unregister_all(self) -> int:
|
|
218
|
+
"""Unregister all MCP tools
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Number of tools unregistered
|
|
222
|
+
"""
|
|
223
|
+
count = 0
|
|
224
|
+
for name in list(self._registered_tools.keys()):
|
|
225
|
+
if self.registry.unregister(name):
|
|
226
|
+
count += 1
|
|
227
|
+
del self._registered_tools[name]
|
|
228
|
+
return count
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def integrate_mcp_with_agent(agent, mcp_manager) -> MCPBridge:
|
|
232
|
+
"""Integrate MCP tools with an agent
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
agent: Agent instance
|
|
236
|
+
mcp_manager: MCPManager instance
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
MCPBridge instance
|
|
240
|
+
"""
|
|
241
|
+
bridge = MCPBridge(agent.registry)
|
|
242
|
+
bridge.connect_mcp_manager(mcp_manager)
|
|
243
|
+
count = bridge.discover_and_register_tools()
|
|
244
|
+
print(f"Registered {count} MCP tools with agent")
|
|
245
|
+
return bridge
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool Permissions System
|
|
3
|
+
|
|
4
|
+
Manages permissions for tool execution with configurable policies.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, List, Optional, Set
|
|
12
|
+
|
|
13
|
+
from .tools.base import ToolPermission, ToolRegistry
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PermissionPolicy(Enum):
|
|
17
|
+
"""Permission policies for different contexts"""
|
|
18
|
+
STRICT = "strict" # Ask for everything
|
|
19
|
+
NORMAL = "normal" # Ask for writes/executes, auto for reads
|
|
20
|
+
PERMISSIVE = "permissive" # Auto-approve most, ask for dangerous
|
|
21
|
+
TRUST = "trust" # Auto-approve everything
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class PermissionRule:
|
|
26
|
+
"""A rule for tool permissions"""
|
|
27
|
+
tool_pattern: str # Tool name or pattern (supports wildcards)
|
|
28
|
+
permission: ToolPermission
|
|
29
|
+
reason: Optional[str] = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class PermissionConfig:
|
|
34
|
+
"""Configuration for the permissions system"""
|
|
35
|
+
policy: PermissionPolicy = PermissionPolicy.NORMAL
|
|
36
|
+
custom_rules: List[PermissionRule] = field(default_factory=list)
|
|
37
|
+
blocked_tools: Set[str] = field(default_factory=set)
|
|
38
|
+
session_approvals: Set[str] = field(default_factory=set)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PermissionManager:
|
|
42
|
+
"""
|
|
43
|
+
Manages tool permissions with configurable policies.
|
|
44
|
+
|
|
45
|
+
Provides:
|
|
46
|
+
- Policy-based default permissions
|
|
47
|
+
- Custom per-tool rules
|
|
48
|
+
- Session-level approvals
|
|
49
|
+
- Blocking of dangerous tools
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
# Default permissions by policy
|
|
53
|
+
POLICY_DEFAULTS = {
|
|
54
|
+
PermissionPolicy.STRICT: {
|
|
55
|
+
"default": ToolPermission.ASK,
|
|
56
|
+
"auto": [],
|
|
57
|
+
},
|
|
58
|
+
PermissionPolicy.NORMAL: {
|
|
59
|
+
"default": ToolPermission.ASK,
|
|
60
|
+
"auto": ["Read", "Glob", "Grep", "Ripgrep", "TodoWrite"],
|
|
61
|
+
},
|
|
62
|
+
PermissionPolicy.PERMISSIVE: {
|
|
63
|
+
"default": ToolPermission.AUTO,
|
|
64
|
+
"ask": ["Bash", "Write", "Edit", "Task", "BackgroundBash"],
|
|
65
|
+
},
|
|
66
|
+
PermissionPolicy.TRUST: {
|
|
67
|
+
"default": ToolPermission.AUTO,
|
|
68
|
+
"ask": [],
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
def __init__(self, config: PermissionConfig = None):
|
|
73
|
+
"""Initialize the permission manager
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
config: Permission configuration
|
|
77
|
+
"""
|
|
78
|
+
self.config = config or PermissionConfig()
|
|
79
|
+
self._registry: Optional[ToolRegistry] = None
|
|
80
|
+
|
|
81
|
+
def attach_registry(self, registry: ToolRegistry) -> None:
|
|
82
|
+
"""Attach a tool registry and apply permissions
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
registry: Tool registry to manage
|
|
86
|
+
"""
|
|
87
|
+
self._registry = registry
|
|
88
|
+
self._apply_policy()
|
|
89
|
+
self._apply_custom_rules()
|
|
90
|
+
|
|
91
|
+
def _apply_policy(self) -> None:
|
|
92
|
+
"""Apply the current policy to the registry"""
|
|
93
|
+
if not self._registry:
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
policy_config = self.POLICY_DEFAULTS.get(self.config.policy, {})
|
|
97
|
+
default_perm = policy_config.get("default", ToolPermission.ASK)
|
|
98
|
+
|
|
99
|
+
# Set default for all tools
|
|
100
|
+
for tool_name in self._registry.list_names():
|
|
101
|
+
self._registry.set_permission(tool_name, default_perm)
|
|
102
|
+
|
|
103
|
+
# Apply auto-approve list
|
|
104
|
+
for tool_name in policy_config.get("auto", []):
|
|
105
|
+
self._registry.set_permission(tool_name, ToolPermission.AUTO)
|
|
106
|
+
|
|
107
|
+
# Apply ask list
|
|
108
|
+
for tool_name in policy_config.get("ask", []):
|
|
109
|
+
self._registry.set_permission(tool_name, ToolPermission.ASK)
|
|
110
|
+
|
|
111
|
+
def _apply_custom_rules(self) -> None:
|
|
112
|
+
"""Apply custom permission rules"""
|
|
113
|
+
if not self._registry:
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
for rule in self.config.custom_rules:
|
|
117
|
+
# Handle wildcards
|
|
118
|
+
if "*" in rule.tool_pattern:
|
|
119
|
+
import fnmatch
|
|
120
|
+
for tool_name in self._registry.list_names():
|
|
121
|
+
if fnmatch.fnmatch(tool_name, rule.tool_pattern):
|
|
122
|
+
self._registry.set_permission(tool_name, rule.permission)
|
|
123
|
+
else:
|
|
124
|
+
self._registry.set_permission(rule.tool_pattern, rule.permission)
|
|
125
|
+
|
|
126
|
+
# Apply blocked tools
|
|
127
|
+
for tool_name in self.config.blocked_tools:
|
|
128
|
+
self._registry.set_permission(tool_name, ToolPermission.DENY)
|
|
129
|
+
|
|
130
|
+
def set_policy(self, policy: PermissionPolicy) -> None:
|
|
131
|
+
"""Change the permission policy
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
policy: New policy to apply
|
|
135
|
+
"""
|
|
136
|
+
self.config.policy = policy
|
|
137
|
+
self._apply_policy()
|
|
138
|
+
self._apply_custom_rules()
|
|
139
|
+
|
|
140
|
+
def add_rule(self, rule: PermissionRule) -> None:
|
|
141
|
+
"""Add a custom permission rule
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
rule: Rule to add
|
|
145
|
+
"""
|
|
146
|
+
self.config.custom_rules.append(rule)
|
|
147
|
+
if self._registry:
|
|
148
|
+
self._apply_custom_rules()
|
|
149
|
+
|
|
150
|
+
def block_tool(self, tool_name: str) -> None:
|
|
151
|
+
"""Block a tool from being used
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
tool_name: Tool to block
|
|
155
|
+
"""
|
|
156
|
+
self.config.blocked_tools.add(tool_name)
|
|
157
|
+
if self._registry:
|
|
158
|
+
self._registry.set_permission(tool_name, ToolPermission.DENY)
|
|
159
|
+
|
|
160
|
+
def unblock_tool(self, tool_name: str) -> None:
|
|
161
|
+
"""Unblock a tool
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
tool_name: Tool to unblock
|
|
165
|
+
"""
|
|
166
|
+
self.config.blocked_tools.discard(tool_name)
|
|
167
|
+
if self._registry:
|
|
168
|
+
self._apply_policy()
|
|
169
|
+
self._apply_custom_rules()
|
|
170
|
+
|
|
171
|
+
def approve_for_session(self, tool_name: str) -> None:
|
|
172
|
+
"""Approve a tool for the current session
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
tool_name: Tool to approve
|
|
176
|
+
"""
|
|
177
|
+
self.config.session_approvals.add(tool_name)
|
|
178
|
+
if self._registry:
|
|
179
|
+
self._registry.approve_for_session(tool_name)
|
|
180
|
+
|
|
181
|
+
def clear_session_approvals(self) -> None:
|
|
182
|
+
"""Clear all session approvals"""
|
|
183
|
+
self.config.session_approvals.clear()
|
|
184
|
+
if self._registry:
|
|
185
|
+
self._registry.clear_session_approvals()
|
|
186
|
+
|
|
187
|
+
def needs_approval(self, tool_name: str) -> bool:
|
|
188
|
+
"""Check if a tool needs approval
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
tool_name: Tool to check
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
True if approval is needed
|
|
195
|
+
"""
|
|
196
|
+
if not self._registry:
|
|
197
|
+
return True
|
|
198
|
+
|
|
199
|
+
if tool_name in self.config.blocked_tools:
|
|
200
|
+
return True # Will be denied
|
|
201
|
+
|
|
202
|
+
if tool_name in self.config.session_approvals:
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
return self._registry.needs_approval(tool_name)
|
|
206
|
+
|
|
207
|
+
def get_permission(self, tool_name: str) -> ToolPermission:
|
|
208
|
+
"""Get the current permission for a tool
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
tool_name: Tool name
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Current permission level
|
|
215
|
+
"""
|
|
216
|
+
if tool_name in self.config.blocked_tools:
|
|
217
|
+
return ToolPermission.DENY
|
|
218
|
+
|
|
219
|
+
if self._registry:
|
|
220
|
+
return self._registry.get_permission(tool_name)
|
|
221
|
+
|
|
222
|
+
return ToolPermission.ASK
|
|
223
|
+
|
|
224
|
+
def get_status(self) -> Dict:
|
|
225
|
+
"""Get current permission status
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Dict with permission information
|
|
229
|
+
"""
|
|
230
|
+
tools_by_permission = {
|
|
231
|
+
"auto": [],
|
|
232
|
+
"ask": [],
|
|
233
|
+
"ask_once": [],
|
|
234
|
+
"deny": [],
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if self._registry:
|
|
238
|
+
for tool_name in self._registry.list_names():
|
|
239
|
+
perm = self.get_permission(tool_name)
|
|
240
|
+
tools_by_permission[perm.value].append(tool_name)
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
"policy": self.config.policy.value,
|
|
244
|
+
"tools": tools_by_permission,
|
|
245
|
+
"blocked": list(self.config.blocked_tools),
|
|
246
|
+
"session_approved": list(self.config.session_approvals),
|
|
247
|
+
"custom_rules": len(self.config.custom_rules),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
def save_config(self, path: str) -> None:
|
|
251
|
+
"""Save permission configuration to file
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
path: Path to save config
|
|
255
|
+
"""
|
|
256
|
+
config_data = {
|
|
257
|
+
"policy": self.config.policy.value,
|
|
258
|
+
"custom_rules": [
|
|
259
|
+
{
|
|
260
|
+
"tool_pattern": r.tool_pattern,
|
|
261
|
+
"permission": r.permission.value,
|
|
262
|
+
"reason": r.reason,
|
|
263
|
+
}
|
|
264
|
+
for r in self.config.custom_rules
|
|
265
|
+
],
|
|
266
|
+
"blocked_tools": list(self.config.blocked_tools),
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
with open(path, "w") as f:
|
|
270
|
+
json.dump(config_data, f, indent=2)
|
|
271
|
+
|
|
272
|
+
@classmethod
|
|
273
|
+
def load_config(cls, path: str) -> "PermissionManager":
|
|
274
|
+
"""Load permission configuration from file
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
path: Path to config file
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
PermissionManager with loaded config
|
|
281
|
+
"""
|
|
282
|
+
with open(path, "r") as f:
|
|
283
|
+
data = json.load(f)
|
|
284
|
+
|
|
285
|
+
config = PermissionConfig(
|
|
286
|
+
policy=PermissionPolicy(data.get("policy", "normal")),
|
|
287
|
+
custom_rules=[
|
|
288
|
+
PermissionRule(
|
|
289
|
+
tool_pattern=r["tool_pattern"],
|
|
290
|
+
permission=ToolPermission(r["permission"]),
|
|
291
|
+
reason=r.get("reason"),
|
|
292
|
+
)
|
|
293
|
+
for r in data.get("custom_rules", [])
|
|
294
|
+
],
|
|
295
|
+
blocked_tools=set(data.get("blocked_tools", [])),
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
return cls(config)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NC1709 Agent Tools
|
|
3
|
+
|
|
4
|
+
Built-in tools for the agentic CLI:
|
|
5
|
+
- File operations: Read, Write, Edit, Glob
|
|
6
|
+
- Search operations: Grep, Ripgrep
|
|
7
|
+
- Execution: Bash, BackgroundBash
|
|
8
|
+
- Sub-agents: Task, TodoWrite, AskUser
|
|
9
|
+
- Web: WebFetch, WebSearch, WebScreenshot
|
|
10
|
+
- Notebook: NotebookRead, NotebookEdit, NotebookRun
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .base import Tool, ToolResult, ToolRegistry, ToolPermission, ToolParameter
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"Tool",
|
|
17
|
+
"ToolResult",
|
|
18
|
+
"ToolRegistry",
|
|
19
|
+
"ToolPermission",
|
|
20
|
+
"ToolParameter",
|
|
21
|
+
]
|