feat(skill): unify skill activation as authorized tools

Expose skill tools (activate/register_skill/native exec) like native tools
instead of gating them behind the skill_authoring capability:
- toolmgr.get_all_tools drops include_skill_authoring; SkillToolLoader
  self-gates on sandbox + skill_mgr
- preproc drops the include_skill_authoring branch; pipeline-bound skills
  and the skills resource gate on skill_mgr presence

Persist activated skills into host.activated_skills conversation state so
they survive across runs (host writes at activate; last-write-wins); drop
the dead restore_activated_skills helper.

Prefill ToolResource.parameters host-side (tool_mgr.get_tool_schema) so
runners build LLM tools without per-tool get_tool_detail round-trips.

Align agent-runner-pluginization design docs to the all-tool model.
This commit is contained in:
huanghuoguoguo
2026-06-21 09:27:05 +08:00
parent cede35b31b
commit 190028d5ab
13 changed files with 210 additions and 62 deletions
+9 -7
View File
@@ -150,7 +150,7 @@ def _import_preproc_modules():
@pytest.mark.asyncio
async def test_preproc_enables_skill_authoring_tools_when_skill_service_available():
async def test_preproc_loads_host_tools_for_runner():
preproc_module, entities_module = _import_preproc_modules()
app = _make_app(skill_service=SimpleNamespace())
@@ -159,7 +159,7 @@ async def test_preproc_enables_skill_authoring_tools_when_skill_service_availabl
result = await stage.process(_make_query(), 'PreProcessor')
assert result.result_type == entities_module.ResultType.CONTINUE
app.tool_mgr.get_all_tools.assert_awaited_once_with(None, None, include_skill_authoring=True)
app.tool_mgr.get_all_tools.assert_awaited_once_with(None, None)
@pytest.mark.asyncio
@@ -180,12 +180,13 @@ async def test_preproc_puts_host_skill_tools_into_query_scope():
result = await stage.process(query, 'PreProcessor')
assert result.result_type == entities_module.ResultType.CONTINUE
app.tool_mgr.get_all_tools.assert_awaited_once_with(None, None, include_skill_authoring=True)
app.tool_mgr.get_all_tools.assert_awaited_once_with(None, None)
assert [tool.name for tool in query.use_funcs] == ['activate', 'register_skill']
@pytest.mark.asyncio
async def test_preproc_disables_skill_authoring_tools_when_skill_service_missing():
async def test_preproc_loads_host_tools_regardless_of_skill_service():
"""Skill tooling no longer gates on skill_service at the preproc layer."""
preproc_module, entities_module = _import_preproc_modules()
app = _make_app(skill_service=None)
@@ -194,7 +195,7 @@ async def test_preproc_disables_skill_authoring_tools_when_skill_service_missing
result = await stage.process(_make_query(), 'PreProcessor')
assert result.result_type == entities_module.ResultType.CONTINUE
app.tool_mgr.get_all_tools.assert_awaited_once_with(None, None, include_skill_authoring=False)
app.tool_mgr.get_all_tools.assert_awaited_once_with(None, None)
@pytest.mark.asyncio
@@ -237,10 +238,11 @@ async def test_preproc_respects_pipeline_bound_skills_subset():
@pytest.mark.asyncio
async def test_preproc_does_not_load_skill_preferences_without_skill_authoring_service():
async def test_preproc_does_not_load_skill_preferences_without_skill_mgr():
preproc_module, entities_module = _import_preproc_modules()
app = _make_app(skill_service=None)
app = _make_app(skill_service=SimpleNamespace())
app.skill_mgr = None # no skill manager -> skill tooling unavailable
query = _make_query()
result = await stage_process_capture(preproc_module, app, query)