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 dcec8f654a
commit 0bd50843fe
14 changed files with 213 additions and 64 deletions
+11 -8
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())
@@ -162,7 +162,6 @@ async def test_preproc_enables_skill_authoring_tools_when_skill_service_availabl
app.tool_mgr.get_all_tools.assert_awaited_once_with(
None,
None,
include_skill_authoring=True,
include_mcp_resource_tools=True,
)
@@ -185,12 +184,17 @@ 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,
include_mcp_resource_tools=True,
)
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)
@@ -202,7 +206,6 @@ async def test_preproc_disables_skill_authoring_tools_when_skill_service_missing
app.tool_mgr.get_all_tools.assert_awaited_once_with(
None,
None,
include_skill_authoring=False,
include_mcp_resource_tools=True,
)
@@ -222,7 +225,6 @@ async def test_preproc_disables_mcp_resource_tools_when_agent_reading_is_disable
app.tool_mgr.get_all_tools.assert_awaited_once_with(
None,
None,
include_skill_authoring=True,
include_mcp_resource_tools=False,
)
@@ -267,10 +269,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)