feat(agents,claude_sdk): skill sets per conversation kind

This commit is contained in:
hh
2026-09-01 23:13:21 +02:00
parent 1954a6e9f2
commit f6a7ea8f29
3 changed files with 76 additions and 19 deletions
+39 -10
View File
@@ -25,7 +25,7 @@ from claude_agent_sdk import (
)
from beaver_gateway.agents.base import ExposedMcp
from beaver_gateway.agents.claude import ClaudeAgent, ClaudeOptions, Prompts
from beaver_gateway.agents.claude import ClaudeAgent, ClaudeOptions, Prompts, SkillSets
from beaver_gateway.backends.claude_sdk import (
ClaudeSdkBackend,
RunnerConfig,
@@ -355,29 +355,58 @@ async def test_mcp_deny_and_usage_sink(cwd: Path) -> None:
assert seen[0].session_id == "fresh-session"
async def test_skill_sets_become_sorted_plugins(cwd: Path) -> None:
def _skill_sets(cwd: Path, *names: str) -> tuple[Path, ...]:
sets = cwd / "skills"
for name in ("zeta", "общие"):
for name in names:
(sets / name / "demo").mkdir(parents=True)
(sets / name / "demo" / "SKILL.md").write_text("---\nname: demo\n---\n")
backend = _backend(
cwd, InMemorySessionStore(), skill_sets=(sets / "zeta", sets / "общие")
)
return tuple(sets / name for name in names)
async def _plugins_for(backend: ClaudeSdkBackend, kind: str) -> list[Path]:
await _drain(
backend.complete(
agent=backend.agent,
messages=[{"role": "user", "content": "x"}],
conversation_id="c",
conversation_id=f"c-{kind}",
kind=kind,
)
)
plugins = FakeClient.instances[0].options.plugins
assert [p["type"] for p in plugins] == ["local", "local"]
plugins = FakeClient.instances[-1].options.plugins
assert all(p["type"] == "local" for p in plugins)
paths = [Path(p["path"]) for p in plugins]
assert [p.name for p in paths] == ["zeta", "общие"]
for path in paths:
assert path.parent.name == kind
assert path.parent.parent.name == backend.agent.name
assert (path / ".claude-plugin" / "plugin.json").exists()
assert (path / "skills" / "demo" / "SKILL.md").exists()
return paths
async def test_skill_sets_become_sorted_plugins(cwd: Path) -> None:
zeta, common = _skill_sets(cwd, "zeta", "общие")
backend = _backend(cwd, InMemorySessionStore(), skill_sets=(zeta, common))
paths = await _plugins_for(backend, "deep")
assert [p.name for p in paths] == ["zeta", "общие"]
assert FakeClient.instances[0].options.skills == "all"
assert [p.name for p in await _plugins_for(backend, "job")] == ["zeta", "общие"]
async def test_skill_sets_per_kind(cwd: Path) -> None:
a, b = _skill_sets(cwd, "a", "b")
backend = _backend(
cwd,
InMemorySessionStore(),
kinds=("master", "branch", "fork"),
skill_sets=SkillSets(master=(a,), branch=(a, b)),
)
master = await _plugins_for(backend, "master")
branch = await _plugins_for(backend, "branch")
assert [p.name for p in master] == ["a"]
assert [p.name for p in branch] == ["a", "b"]
assert master[0] != branch[0]
assert await _plugins_for(backend, "fork") == []
assert FakeClient.instances[-1].options.skills is None
async def test_prompt_sources_are_assembled(cwd: Path) -> None: