skilllite 0.1.1__py3-none-any.whl → 0.1.2__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.
- skilllite/core/__init__.py +2 -0
- skilllite/core/adapters/__init__.py +74 -0
- skilllite/core/adapters/langchain.py +362 -0
- skilllite/core/adapters/llamaindex.py +264 -0
- skilllite/core/handler.py +179 -4
- skilllite/core/loops.py +175 -13
- skilllite/core/manager.py +82 -15
- skilllite/core/metadata.py +14 -7
- skilllite/core/security.py +420 -0
- skilllite/mcp/server.py +266 -58
- skilllite/quick.py +14 -4
- skilllite/sandbox/context.py +155 -0
- skilllite/sandbox/execution_service.py +254 -0
- skilllite/sandbox/skillbox/executor.py +124 -19
- skilllite/sandbox/unified_executor.py +359 -0
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/METADATA +98 -1
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/RECORD +21 -14
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/WHEEL +0 -0
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/entry_points.txt +0 -0
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/top_level.txt +0 -0
skilllite/core/loops.py
CHANGED
|
@@ -56,11 +56,12 @@ class AgenticLoop:
|
|
|
56
56
|
custom_tool_handler: Optional[Callable] = None,
|
|
57
57
|
enable_task_planning: bool = True,
|
|
58
58
|
verbose: bool = True,
|
|
59
|
+
confirmation_callback: Optional[Callable[[str, str], bool]] = None,
|
|
59
60
|
**kwargs
|
|
60
61
|
):
|
|
61
62
|
"""
|
|
62
63
|
Initialize the agentic loop.
|
|
63
|
-
|
|
64
|
+
|
|
64
65
|
Args:
|
|
65
66
|
manager: SkillManager instance
|
|
66
67
|
client: LLM client (OpenAI or Anthropic)
|
|
@@ -71,6 +72,9 @@ class AgenticLoop:
|
|
|
71
72
|
custom_tool_handler: Optional custom tool handler function
|
|
72
73
|
enable_task_planning: Whether to generate task list before execution
|
|
73
74
|
verbose: Whether to print detailed logs
|
|
75
|
+
confirmation_callback: Callback for security confirmation (sandbox_level=3).
|
|
76
|
+
Signature: (security_report: str, scan_id: str) -> bool
|
|
77
|
+
If None and sandbox_level=3, will use interactive terminal confirmation.
|
|
74
78
|
**kwargs: Additional arguments passed to the LLM
|
|
75
79
|
"""
|
|
76
80
|
self.manager = manager
|
|
@@ -82,14 +86,157 @@ class AgenticLoop:
|
|
|
82
86
|
self.custom_tool_handler = custom_tool_handler
|
|
83
87
|
self.enable_task_planning = enable_task_planning
|
|
84
88
|
self.verbose = verbose
|
|
89
|
+
self.confirmation_callback = confirmation_callback
|
|
85
90
|
self.extra_kwargs = kwargs
|
|
86
91
|
self.task_list: List[Dict] = []
|
|
87
|
-
|
|
92
|
+
|
|
93
|
+
# Initialize security scanner for sandbox_level=3
|
|
94
|
+
self._security_scanner = None
|
|
95
|
+
self._pending_confirmation = False # Track if confirmation is pending
|
|
96
|
+
|
|
88
97
|
def _log(self, message: str) -> None:
|
|
89
98
|
"""Print log message if verbose mode is enabled."""
|
|
90
99
|
if self.verbose:
|
|
91
100
|
print(message)
|
|
92
|
-
|
|
101
|
+
|
|
102
|
+
def _get_security_scanner(self):
|
|
103
|
+
"""Get or lazily initialize the security scanner."""
|
|
104
|
+
if self._security_scanner is None:
|
|
105
|
+
from .security import SecurityScanner
|
|
106
|
+
self._security_scanner = SecurityScanner()
|
|
107
|
+
return self._security_scanner
|
|
108
|
+
|
|
109
|
+
def _should_perform_security_scan(self) -> bool:
|
|
110
|
+
"""Check if security scanning should be performed."""
|
|
111
|
+
import os
|
|
112
|
+
sandbox_level = os.environ.get("SKILLBOX_SANDBOX_LEVEL", "3")
|
|
113
|
+
return sandbox_level == "3" and self.confirmation_callback is not None
|
|
114
|
+
|
|
115
|
+
def _interactive_confirmation(self, report: str, scan_id: str) -> bool:
|
|
116
|
+
"""Default interactive terminal confirmation."""
|
|
117
|
+
self._log(f"\n{report}")
|
|
118
|
+
self._log("\n" + "=" * 60)
|
|
119
|
+
while True:
|
|
120
|
+
response = input("⚠️ Allow execution? (y/n): ").strip().lower()
|
|
121
|
+
if response in ['y', 'yes']:
|
|
122
|
+
return True
|
|
123
|
+
elif response in ['n', 'no']:
|
|
124
|
+
return False
|
|
125
|
+
self._log("Please enter 'y' or 'n'")
|
|
126
|
+
|
|
127
|
+
def _perform_security_confirmation_for_tools(
|
|
128
|
+
self,
|
|
129
|
+
tool_calls: List[Any]
|
|
130
|
+
) -> bool:
|
|
131
|
+
"""
|
|
132
|
+
Perform security scan and confirmation for tool calls.
|
|
133
|
+
|
|
134
|
+
Returns True if execution should proceed, False if denied.
|
|
135
|
+
Also handles skills that require elevated permissions.
|
|
136
|
+
"""
|
|
137
|
+
import os
|
|
138
|
+
from .security import SecurityScanResult
|
|
139
|
+
|
|
140
|
+
sandbox_level = os.environ.get("SKILLBOX_SANDBOX_LEVEL", "3")
|
|
141
|
+
if sandbox_level != "3":
|
|
142
|
+
return True # No confirmation needed for levels 1-2
|
|
143
|
+
|
|
144
|
+
# Get skill tool names
|
|
145
|
+
skill_tool_names = set(self.manager.skill_names())
|
|
146
|
+
skill_tool_names.update(self.manager._registry.list_multi_script_tools())
|
|
147
|
+
|
|
148
|
+
# Scan each skill tool call
|
|
149
|
+
combined_issues = []
|
|
150
|
+
scanned_skills = set()
|
|
151
|
+
requires_elevated = False # Track if any skill requires elevated permissions
|
|
152
|
+
|
|
153
|
+
for tc in tool_calls:
|
|
154
|
+
tool_name = tc.function.name if hasattr(tc, 'function') else tc.get('name', '')
|
|
155
|
+
|
|
156
|
+
# Only scan skill tools, not custom tools
|
|
157
|
+
if tool_name not in skill_tool_names:
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
# Get skill info
|
|
161
|
+
skill_name = tool_name.split('__')[0] if '__' in tool_name else tool_name
|
|
162
|
+
if skill_name in scanned_skills:
|
|
163
|
+
continue
|
|
164
|
+
scanned_skills.add(skill_name)
|
|
165
|
+
|
|
166
|
+
skill_info = self.manager.get_skill(skill_name)
|
|
167
|
+
if not skill_info:
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
# Check if skill requires elevated permissions
|
|
171
|
+
if skill_info.metadata and getattr(skill_info.metadata, 'requires_elevated_permissions', False):
|
|
172
|
+
requires_elevated = True
|
|
173
|
+
self._log(f"🔓 Skill '{skill_name}' requires elevated permissions")
|
|
174
|
+
|
|
175
|
+
# Parse input data
|
|
176
|
+
try:
|
|
177
|
+
import json
|
|
178
|
+
input_data = json.loads(tc.function.arguments) if hasattr(tc, 'function') else {}
|
|
179
|
+
except (json.JSONDecodeError, AttributeError):
|
|
180
|
+
input_data = {}
|
|
181
|
+
|
|
182
|
+
# Perform security scan
|
|
183
|
+
scanner = self._get_security_scanner()
|
|
184
|
+
result = scanner.scan_skill(skill_info, input_data)
|
|
185
|
+
combined_issues.extend(result.issues)
|
|
186
|
+
|
|
187
|
+
# If skill requires elevated permissions, downgrade sandbox level
|
|
188
|
+
if requires_elevated and not combined_issues:
|
|
189
|
+
self._log("✅ Skill requires elevated permissions, downgrading to sandbox level 1...")
|
|
190
|
+
os.environ["SKILLBOX_SANDBOX_LEVEL"] = "1"
|
|
191
|
+
self._pending_confirmation = True
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
if not combined_issues:
|
|
195
|
+
return True # No issues found
|
|
196
|
+
|
|
197
|
+
# Create combined scan result
|
|
198
|
+
high_count = sum(1 for i in combined_issues if i.get("severity") in ["Critical", "High"])
|
|
199
|
+
medium_count = sum(1 for i in combined_issues if i.get("severity") == "Medium")
|
|
200
|
+
low_count = sum(1 for i in combined_issues if i.get("severity") == "Low")
|
|
201
|
+
|
|
202
|
+
combined_result = SecurityScanResult(
|
|
203
|
+
is_safe=high_count == 0,
|
|
204
|
+
issues=combined_issues,
|
|
205
|
+
scan_id=f"batch-{len(scanned_skills)}",
|
|
206
|
+
high_severity_count=high_count,
|
|
207
|
+
medium_severity_count=medium_count,
|
|
208
|
+
low_severity_count=low_count,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if not combined_result.requires_confirmation:
|
|
212
|
+
return True # Only low/medium issues, proceed
|
|
213
|
+
|
|
214
|
+
# Ask for confirmation
|
|
215
|
+
report = combined_result.format_report()
|
|
216
|
+
self._log(f"\n🔒 Security scan detected potential issues:")
|
|
217
|
+
|
|
218
|
+
if self.confirmation_callback:
|
|
219
|
+
confirmed = self.confirmation_callback(report, combined_result.scan_id)
|
|
220
|
+
else:
|
|
221
|
+
confirmed = self._interactive_confirmation(report, combined_result.scan_id)
|
|
222
|
+
|
|
223
|
+
if confirmed:
|
|
224
|
+
# Temporarily downgrade sandbox level to allow execution
|
|
225
|
+
self._log("✅ User confirmed. Executing with sandbox level 1...")
|
|
226
|
+
os.environ["SKILLBOX_SANDBOX_LEVEL"] = "1"
|
|
227
|
+
self._pending_confirmation = True
|
|
228
|
+
return True
|
|
229
|
+
else:
|
|
230
|
+
self._log("❌ User denied execution.")
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
def _restore_sandbox_level(self, original_level: str) -> None:
|
|
234
|
+
"""Restore original sandbox level after execution."""
|
|
235
|
+
import os
|
|
236
|
+
if self._pending_confirmation:
|
|
237
|
+
os.environ["SKILLBOX_SANDBOX_LEVEL"] = original_level
|
|
238
|
+
self._pending_confirmation = False
|
|
239
|
+
|
|
93
240
|
def _get_execution_system_prompt(self) -> str:
|
|
94
241
|
"""
|
|
95
242
|
Generate the main execution system prompt for skill selection and file operations.
|
|
@@ -573,17 +720,25 @@ Based on the documentation, call the tools with correct parameters.
|
|
|
573
720
|
continue
|
|
574
721
|
|
|
575
722
|
messages.append(message)
|
|
576
|
-
|
|
723
|
+
|
|
724
|
+
# Execute tools using unified execution service
|
|
577
725
|
self._log(f"\n⚙️ Executing tools...")
|
|
726
|
+
|
|
578
727
|
if self.custom_tool_handler:
|
|
728
|
+
# Custom tool handler takes precedence
|
|
579
729
|
tool_results = self.custom_tool_handler(
|
|
580
730
|
response, self.manager, allow_network, timeout
|
|
581
731
|
)
|
|
582
732
|
else:
|
|
583
|
-
|
|
584
|
-
|
|
733
|
+
# Use unified execution service with confirmation callback
|
|
734
|
+
# This handles security scanning, confirmation, and sandbox level management
|
|
735
|
+
tool_results = self.manager.handle_tool_calls_with_unified_service(
|
|
736
|
+
response,
|
|
737
|
+
confirmation_callback=self.confirmation_callback or self._interactive_confirmation,
|
|
738
|
+
allow_network=allow_network,
|
|
739
|
+
timeout=timeout
|
|
585
740
|
)
|
|
586
|
-
|
|
741
|
+
|
|
587
742
|
self._log(f"\n📊 Tool execution results:")
|
|
588
743
|
for idx, (result, tc) in enumerate(zip(tool_results, message.tool_calls), 1):
|
|
589
744
|
output = result.content
|
|
@@ -591,7 +746,7 @@ Based on the documentation, call the tools with correct parameters.
|
|
|
591
746
|
output = output[:500] + "... (truncated)"
|
|
592
747
|
self._log(f" {idx}. {tc.function.name}")
|
|
593
748
|
self._log(f" Result: {output}")
|
|
594
|
-
|
|
749
|
+
|
|
595
750
|
for result in tool_results:
|
|
596
751
|
messages.append(result.to_openai_format())
|
|
597
752
|
|
|
@@ -693,19 +848,26 @@ Based on the documentation, call the tools with correct parameters.
|
|
|
693
848
|
self._log(f" Arguments: {json.dumps(block.input, ensure_ascii=False)}")
|
|
694
849
|
|
|
695
850
|
messages.append({"role": "assistant", "content": response.content})
|
|
696
|
-
|
|
851
|
+
|
|
852
|
+
# Execute tools using unified execution service
|
|
697
853
|
self._log(f"\n⚙️ Executing tools...")
|
|
698
|
-
|
|
699
|
-
|
|
854
|
+
|
|
855
|
+
# Use unified execution service with confirmation callback
|
|
856
|
+
# This handles security scanning, confirmation, and sandbox level management
|
|
857
|
+
tool_results = self.manager.handle_tool_calls_claude_native_with_unified_service(
|
|
858
|
+
response,
|
|
859
|
+
confirmation_callback=self.confirmation_callback or self._interactive_confirmation,
|
|
860
|
+
allow_network=allow_network,
|
|
861
|
+
timeout=timeout
|
|
700
862
|
)
|
|
701
|
-
|
|
863
|
+
|
|
702
864
|
self._log(f"\n📊 Tool execution results:")
|
|
703
865
|
for idx, result in enumerate(tool_results, 1):
|
|
704
866
|
output = result.content
|
|
705
867
|
if len(output) > 500:
|
|
706
868
|
output = output[:500] + "... (truncated)"
|
|
707
869
|
self._log(f" {idx}. Result: {output}")
|
|
708
|
-
|
|
870
|
+
|
|
709
871
|
formatted_results = self.manager.format_tool_results_claude_native(tool_results)
|
|
710
872
|
messages.append({"role": "user", "content": formatted_results})
|
|
711
873
|
|
skilllite/core/manager.py
CHANGED
|
@@ -304,7 +304,64 @@ class SkillManager:
|
|
|
304
304
|
) -> List[ToolResult]:
|
|
305
305
|
"""Parse and execute all tool calls from Claude's native API response."""
|
|
306
306
|
return self._handler.handle_tool_calls_claude_native(response, allow_network, timeout)
|
|
307
|
-
|
|
307
|
+
|
|
308
|
+
def handle_tool_calls_with_unified_service(
|
|
309
|
+
self,
|
|
310
|
+
response: Any,
|
|
311
|
+
confirmation_callback: Optional[Callable[[str, str], bool]] = None,
|
|
312
|
+
allow_network: Optional[bool] = None,
|
|
313
|
+
timeout: Optional[int] = None
|
|
314
|
+
) -> List[ToolResult]:
|
|
315
|
+
"""
|
|
316
|
+
Parse and execute all tool calls using UnifiedExecutionService.
|
|
317
|
+
|
|
318
|
+
This method uses the unified execution layer which:
|
|
319
|
+
1. Reads sandbox level at runtime
|
|
320
|
+
2. Handles security scanning and confirmation per-skill
|
|
321
|
+
3. Properly downgrades sandbox level after confirmation
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
response: Response from OpenAI-compatible API
|
|
325
|
+
confirmation_callback: Callback for security confirmation
|
|
326
|
+
allow_network: Whether to allow network access
|
|
327
|
+
timeout: Execution timeout in seconds
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
List of ToolResult objects
|
|
331
|
+
"""
|
|
332
|
+
return self._handler.handle_tool_calls_with_unified_service(
|
|
333
|
+
response,
|
|
334
|
+
confirmation_callback=confirmation_callback,
|
|
335
|
+
allow_network=allow_network,
|
|
336
|
+
timeout=timeout
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def handle_tool_calls_claude_native_with_unified_service(
|
|
340
|
+
self,
|
|
341
|
+
response: Any,
|
|
342
|
+
confirmation_callback: Optional[Callable[[str, str], bool]] = None,
|
|
343
|
+
allow_network: Optional[bool] = None,
|
|
344
|
+
timeout: Optional[int] = None
|
|
345
|
+
) -> List[ToolResult]:
|
|
346
|
+
"""
|
|
347
|
+
Parse and execute all Claude tool calls using UnifiedExecutionService.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
response: Response from Claude's native API
|
|
351
|
+
confirmation_callback: Callback for security confirmation
|
|
352
|
+
allow_network: Whether to allow network access
|
|
353
|
+
timeout: Execution timeout in seconds
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
List of ToolResult objects
|
|
357
|
+
"""
|
|
358
|
+
return self._handler.handle_tool_calls_claude_native_with_unified_service(
|
|
359
|
+
response,
|
|
360
|
+
confirmation_callback=confirmation_callback,
|
|
361
|
+
allow_network=allow_network,
|
|
362
|
+
timeout=timeout
|
|
363
|
+
)
|
|
364
|
+
|
|
308
365
|
def format_tool_results_claude_native(self, results: List[ToolResult]) -> List[Dict[str, Any]]:
|
|
309
366
|
"""Format tool results for Claude's native API."""
|
|
310
367
|
return self._handler.format_tool_results_claude_native(results)
|
|
@@ -335,6 +392,7 @@ class SkillManager:
|
|
|
335
392
|
custom_tool_handler: Optional[Callable] = None,
|
|
336
393
|
enable_task_planning: bool = True,
|
|
337
394
|
verbose: bool = True,
|
|
395
|
+
confirmation_callback: Optional[Callable[[str, str], bool]] = None,
|
|
338
396
|
**kwargs
|
|
339
397
|
) -> AgenticLoop:
|
|
340
398
|
"""
|
|
@@ -351,15 +409,16 @@ class SkillManager:
|
|
|
351
409
|
custom_tool_handler: Optional custom tool handler
|
|
352
410
|
enable_task_planning: Whether to generate task list before execution
|
|
353
411
|
verbose: Whether to print detailed logs
|
|
412
|
+
confirmation_callback: Callback for security confirmation (sandbox_level=3)
|
|
354
413
|
**kwargs: Additional arguments passed to the LLM
|
|
355
|
-
|
|
414
|
+
|
|
356
415
|
Returns:
|
|
357
416
|
AgenticLoop instance
|
|
358
|
-
|
|
417
|
+
|
|
359
418
|
Example:
|
|
360
419
|
# OpenAI-compatible (default)
|
|
361
420
|
loop = manager.create_agentic_loop(client, "gpt-4")
|
|
362
|
-
|
|
421
|
+
|
|
363
422
|
# Claude native API
|
|
364
423
|
loop = manager.create_agentic_loop(client, "claude-3-opus",
|
|
365
424
|
api_format="claude_native")
|
|
@@ -375,6 +434,7 @@ class SkillManager:
|
|
|
375
434
|
custom_tool_handler=custom_tool_handler,
|
|
376
435
|
enable_task_planning=enable_task_planning,
|
|
377
436
|
verbose=verbose,
|
|
437
|
+
confirmation_callback=confirmation_callback,
|
|
378
438
|
**kwargs
|
|
379
439
|
)
|
|
380
440
|
|
|
@@ -421,14 +481,15 @@ class SkillManager:
|
|
|
421
481
|
custom_tool_executor: Optional[Callable] = None,
|
|
422
482
|
enable_task_planning: bool = True,
|
|
423
483
|
verbose: bool = True,
|
|
484
|
+
confirmation_callback: Optional[Callable[[str, str], bool]] = None,
|
|
424
485
|
**kwargs
|
|
425
486
|
) -> AgenticLoop:
|
|
426
487
|
"""
|
|
427
488
|
Create an enhanced agentic loop with custom tools support.
|
|
428
|
-
|
|
489
|
+
|
|
429
490
|
This method creates an AgenticLoop that can handle both skill tools
|
|
430
491
|
and custom tools (like file operations).
|
|
431
|
-
|
|
492
|
+
|
|
432
493
|
Args:
|
|
433
494
|
client: LLM client (OpenAI-compatible)
|
|
434
495
|
model: Model name to use
|
|
@@ -438,27 +499,32 @@ class SkillManager:
|
|
|
438
499
|
custom_tool_executor: Executor function for custom tools
|
|
439
500
|
enable_task_planning: Whether to generate task list before execution
|
|
440
501
|
verbose: Whether to print detailed logs
|
|
502
|
+
confirmation_callback: Callback for security confirmation (sandbox_level=3)
|
|
441
503
|
**kwargs: Additional arguments passed to the LLM
|
|
442
|
-
|
|
504
|
+
|
|
443
505
|
Returns:
|
|
444
506
|
AgenticLoop instance with enhanced capabilities
|
|
445
507
|
"""
|
|
446
508
|
# Create custom tool handler that combines skill tools and custom tools
|
|
447
509
|
def combined_tool_handler(response, manager, allow_network, timeout):
|
|
448
510
|
from .tools import ToolUseRequest, ToolResult
|
|
449
|
-
|
|
511
|
+
|
|
450
512
|
requests = ToolUseRequest.parse_from_openai_response(response)
|
|
451
513
|
results = []
|
|
452
|
-
|
|
514
|
+
|
|
453
515
|
# Get skill tool names
|
|
454
516
|
skill_tool_names = set(self.skill_names())
|
|
455
517
|
skill_tool_names.update(self._registry.list_multi_script_tools())
|
|
456
|
-
|
|
518
|
+
|
|
457
519
|
for request in requests:
|
|
458
520
|
if request.name in skill_tool_names:
|
|
459
|
-
# Execute as skill tool
|
|
460
|
-
|
|
461
|
-
|
|
521
|
+
# Execute as skill tool using UnifiedExecutionService
|
|
522
|
+
# This handles security scanning, confirmation, and proper sandbox level
|
|
523
|
+
result = self._handler.execute_tool_call_with_unified_service(
|
|
524
|
+
request,
|
|
525
|
+
confirmation_callback=confirmation_callback,
|
|
526
|
+
allow_network=allow_network,
|
|
527
|
+
timeout=timeout
|
|
462
528
|
)
|
|
463
529
|
results.append(result)
|
|
464
530
|
elif custom_tool_executor:
|
|
@@ -473,9 +539,9 @@ class SkillManager:
|
|
|
473
539
|
results.append(ToolResult.error(
|
|
474
540
|
request.id, f"No executor found for tool: {request.name}"
|
|
475
541
|
))
|
|
476
|
-
|
|
542
|
+
|
|
477
543
|
return results
|
|
478
|
-
|
|
544
|
+
|
|
479
545
|
return AgenticLoop(
|
|
480
546
|
manager=self,
|
|
481
547
|
client=client,
|
|
@@ -486,6 +552,7 @@ class SkillManager:
|
|
|
486
552
|
custom_tool_handler=combined_tool_handler if custom_tool_executor else None,
|
|
487
553
|
enable_task_planning=enable_task_planning,
|
|
488
554
|
verbose=verbose,
|
|
555
|
+
confirmation_callback=confirmation_callback,
|
|
489
556
|
**kwargs
|
|
490
557
|
)
|
|
491
558
|
|
skilllite/core/metadata.py
CHANGED
|
@@ -35,31 +35,37 @@ class SkillMetadata:
|
|
|
35
35
|
network: NetworkPolicy = field(default_factory=NetworkPolicy)
|
|
36
36
|
input_schema: Optional[Dict[str, Any]] = None
|
|
37
37
|
output_schema: Optional[Dict[str, Any]] = None
|
|
38
|
-
|
|
38
|
+
requires_elevated_permissions: bool = False # For skills that need to write outside their directory
|
|
39
|
+
|
|
39
40
|
@classmethod
|
|
40
41
|
def from_dict(cls, data: Dict[str, Any], skill_dir: Optional[Path] = None) -> "SkillMetadata":
|
|
41
42
|
"""Create SkillMetadata from parsed YAML front matter."""
|
|
42
43
|
version = data.get("version")
|
|
43
44
|
if not version and "metadata" in data:
|
|
44
45
|
version = data["metadata"].get("version")
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
compatibility = data.get("compatibility")
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
# Parse network policy from compatibility field
|
|
49
50
|
network = parse_compatibility_for_network(compatibility)
|
|
50
|
-
|
|
51
|
+
|
|
51
52
|
# Auto-detect entry point
|
|
52
53
|
entry_point = ""
|
|
53
54
|
if skill_dir:
|
|
54
55
|
detected = detect_entry_point(skill_dir)
|
|
55
56
|
if detected:
|
|
56
57
|
entry_point = detected
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
# Detect language from compatibility or entry point
|
|
59
60
|
language = parse_compatibility_for_language(compatibility)
|
|
60
61
|
if not language and entry_point:
|
|
61
62
|
language = detect_language_from_entry_point(entry_point)
|
|
62
|
-
|
|
63
|
+
|
|
64
|
+
# Parse requires_elevated_permissions flag
|
|
65
|
+
requires_elevated = data.get("requires_elevated_permissions", False)
|
|
66
|
+
if isinstance(requires_elevated, str):
|
|
67
|
+
requires_elevated = requires_elevated.lower() in ("true", "yes", "1")
|
|
68
|
+
|
|
63
69
|
return cls(
|
|
64
70
|
name=data.get("name", ""),
|
|
65
71
|
entry_point=entry_point,
|
|
@@ -69,7 +75,8 @@ class SkillMetadata:
|
|
|
69
75
|
compatibility=compatibility,
|
|
70
76
|
network=network,
|
|
71
77
|
input_schema=data.get("input_schema"),
|
|
72
|
-
output_schema=data.get("output_schema")
|
|
78
|
+
output_schema=data.get("output_schema"),
|
|
79
|
+
requires_elevated_permissions=bool(requires_elevated)
|
|
73
80
|
)
|
|
74
81
|
|
|
75
82
|
|