fix(skills): harden slash skill activation across chat channels (#3466)

* support slash skill activation

* format slash skill activation

* Preserve slash skill activation with uploads

* Address slash skill review feedback

* Address slash skill follow-up review

* Fix lazy slash skill storage resolution

* Keep slash skill activation out of system prompt

* Address slash skill review issues

* fix: harden slash skill command handling

* feat(frontend): add slash skill autocomplete

* fix: address slash skill review feedback

* fix: preserve slash skill text for IM uploads
This commit is contained in:
DanielWalnut
2026-06-09 23:07:17 +08:00
committed by GitHub
parent 18bbb82f07
commit 16391e35ab
31 changed files with 2758 additions and 57 deletions
@@ -49,6 +49,8 @@ from deerflow.tracing import build_tracing_callbacks
logger = logging.getLogger(__name__)
_BOOTSTRAP_SKILL_NAMES = {"bootstrap"}
def _get_runtime_config(config: RunnableConfig) -> dict:
"""Merge legacy configurable options with LangGraph runtime context."""
@@ -271,6 +273,7 @@ def build_middlewares(
agent_name: str | None = None,
custom_middlewares: list[AgentMiddleware] | None = None,
*,
available_skills: set[str] | None = None,
app_config: AppConfig | None = None,
deferred_setup=None,
):
@@ -302,6 +305,13 @@ def build_middlewares(
middlewares.append(DynamicContextMiddleware(agent_name=agent_name, app_config=resolved_app_config))
# Deterministically load a full SKILL.md when the user starts the turn with
# /skill-name. This keeps the base system prompt metadata-only while giving
# explicit user activation priority over model-side relevance guessing.
from deerflow.agents.middlewares.skill_activation_middleware import SkillActivationMiddleware
middlewares.append(SkillActivationMiddleware(available_skills=available_skills, app_config=resolved_app_config))
# Add summarization middleware if enabled
summarization_middleware = _create_summarization_middleware(app_config=resolved_app_config)
if summarization_middleware is not None:
@@ -369,7 +379,7 @@ def build_middlewares(
def _available_skill_names(agent_config, is_bootstrap: bool) -> set[str] | None:
if is_bootstrap:
return {"bootstrap"}
return set(_BOOTSTRAP_SKILL_NAMES)
if agent_config and agent_config.skills is not None:
return set(agent_config.skills)
return None
@@ -475,17 +485,25 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
if is_bootstrap:
# Special bootstrap agent with minimal prompt for initial custom agent creation flow
# Keep the bootstrap skill set intentionally narrow so agent creation
# remains deterministic before the custom agent's own config exists.
raw_tools = get_available_tools(model_name=model_name, subagent_enabled=subagent_enabled, app_config=resolved_app_config) + [setup_agent]
filtered = filter_tools_by_skill_allowed_tools(raw_tools, skills_for_tool_policy)
final_tools, setup = assemble_deferred_tools(filtered, enabled=resolved_app_config.tool_search.enabled)
return create_agent(
model=create_chat_model(name=model_name, thinking_enabled=thinking_enabled, app_config=resolved_app_config, attach_tracing=False),
tools=final_tools,
middleware=build_middlewares(config, model_name=model_name, app_config=resolved_app_config, deferred_setup=setup),
middleware=build_middlewares(
config,
model_name=model_name,
available_skills=set(_BOOTSTRAP_SKILL_NAMES),
app_config=resolved_app_config,
deferred_setup=setup,
),
system_prompt=apply_prompt_template(
subagent_enabled=subagent_enabled,
max_concurrent_subagents=max_concurrent_subagents,
available_skills=set(["bootstrap"]),
available_skills=set(_BOOTSTRAP_SKILL_NAMES),
app_config=resolved_app_config,
deferred_names=setup.deferred_names,
),
@@ -502,12 +520,19 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
return create_agent(
model=create_chat_model(name=model_name, thinking_enabled=thinking_enabled, reasoning_effort=reasoning_effort, app_config=resolved_app_config, attach_tracing=False),
tools=final_tools,
middleware=build_middlewares(config, model_name=model_name, agent_name=agent_name, app_config=resolved_app_config, deferred_setup=setup),
middleware=build_middlewares(
config,
model_name=model_name,
agent_name=agent_name,
available_skills=available_skills,
app_config=resolved_app_config,
deferred_setup=setup,
),
system_prompt=apply_prompt_template(
subagent_enabled=subagent_enabled,
max_concurrent_subagents=max_concurrent_subagents,
agent_name=agent_name,
available_skills=set(agent_config.skills) if agent_config and agent_config.skills is not None else None,
available_skills=available_skills,
app_config=resolved_app_config,
deferred_names=setup.deferred_names,
),