fix(agent-runner): close sandbox skill authoring flow

This commit is contained in:
huanghuoguoguo
2026-07-15 08:35:52 +08:00
parent d871baab68
commit 730d52bcd0
12 changed files with 313 additions and 31 deletions
@@ -169,7 +169,7 @@ class MonitoringService:
)
row = result.first()
if row:
return row[0]
return row
if not session_id:
return None
@@ -188,7 +188,7 @@ class MonitoringService:
result = await self.ap.persistence_mgr.execute_async(user_query)
row = result.first()
if row:
return row[0]
return row
any_query = (
sqlalchemy.select(persistence_monitoring.MonitoringMessage)
@@ -198,7 +198,7 @@ class MonitoringService:
)
result = await self.ap.persistence_mgr.execute_async(any_query)
row = result.first()
return row[0] if row else None
return row
# ========== Recording Methods ==========
@@ -574,9 +574,13 @@ else:
)
if self._should_use_box_workspace_files(selected_skill):
return await self._write_workspace_via_box(path, content, parameters, query)
os.makedirs(os.path.dirname(host_path), exist_ok=True)
try:
os.makedirs(os.path.dirname(host_path), exist_ok=True)
self._write_host_file(host_path, content, parameters)
except PermissionError:
if selected_skill is None and hasattr(self.ap.box_service, 'execute_tool'):
return await self._write_workspace_via_box(path, content, parameters, query)
raise
except ValueError as exc:
return {'ok': False, 'error': str(exc)}
self._refresh_skill_from_disk(selected_skill)
@@ -625,18 +629,23 @@ else:
)
if self._should_use_box_workspace_files(selected_skill):
return await self._edit_workspace_via_box(path, old_string, new_string, query)
if not os.path.isfile(host_path):
return {'ok': False, 'error': f'File not found: {path}'}
with open(host_path, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
count = content.count(old_string)
if count == 0:
return {'ok': False, 'error': 'old_string not found in file.'}
if count > 1:
return {'ok': False, 'error': f'old_string matches {count} locations; provide a more unique string.'}
new_content = content.replace(old_string, new_string, 1)
with open(host_path, 'w', encoding='utf-8') as f:
f.write(new_content)
try:
if not os.path.isfile(host_path):
return {'ok': False, 'error': f'File not found: {path}'}
with open(host_path, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
count = content.count(old_string)
if count == 0:
return {'ok': False, 'error': 'old_string not found in file.'}
if count > 1:
return {'ok': False, 'error': f'old_string matches {count} locations; provide a more unique string.'}
new_content = content.replace(old_string, new_string, 1)
with open(host_path, 'w', encoding='utf-8') as f:
f.write(new_content)
except PermissionError:
if selected_skill is None and hasattr(self.ap.box_service, 'execute_tool'):
return await self._edit_workspace_via_box(path, old_string, new_string, query)
raise
self._refresh_skill_from_disk(selected_skill)
return {'ok': True, 'path': path}
@@ -49,6 +49,18 @@ def get_visible_skill(ap: app.Application, query: pipeline_query.Query, skill_na
return get_visible_skills(ap, query).get(skill_name)
def register_created_skill_visibility(query: pipeline_query.Query, skill_name: str) -> None:
"""Make a newly registered skill visible for the current Query only."""
if not skill_name:
return
if getattr(query, 'variables', None) is None:
query.variables = {}
bound_skills = query.variables.get(PIPELINE_BOUND_SKILLS_KEY)
if isinstance(bound_skills, list) and skill_name not in bound_skills:
bound_skills.append(skill_name)
def get_activated_skills(query: pipeline_query.Query) -> dict[str, dict]:
if query.variables is None:
return {}
@@ -67,7 +67,7 @@ class SkillToolLoader(loader.ToolLoader):
if name == ACTIVATE_SKILL_TOOL_NAME:
return await self._invoke_activate_skill(parameters, query)
if name == REGISTER_SKILL_TOOL_NAME:
return await self._invoke_register_skill(parameters)
return await self._invoke_register_skill(parameters, query)
raise ValueError(f'Unknown skill tool: {name}')
async def shutdown(self):
@@ -121,8 +121,10 @@ class SkillToolLoader(loader.ToolLoader):
'content': result_content,
}
async def _invoke_register_skill(self, parameters: dict) -> typing.Any:
async def _invoke_register_skill(self, parameters: dict, query) -> typing.Any:
"""Register a skill from sandbox directory to data/skills/."""
from . import skill as skill_loader
sandbox_path = str(parameters.get('path', '') or '').strip()
if not sandbox_path:
raise ValueError('path is required')
@@ -153,6 +155,7 @@ class SkillToolLoader(loader.ToolLoader):
'package_root': host_path,
}
)
skill_loader.register_created_skill_visibility(query, skill_name)
return {
'registered': True,