Skip to content

[refactor] follow-ups deferred from #2666 (app_config threading) #2687

@greatmengqi

Description

@greatmengqi

PR #2666 threaded explicit app_config through the lead-agent prompt and subagent task path, intentionally scoped to bug fixes. The following structural improvements were deferred and are tracked here for follow-up PRs.

Items are roughly ordered by impact/effort.

1. Push sandbox-fallback into is_host_bash_allowed

task_tool.py:105 and subagents/registry.py:157 both forward an optional config to is_host_bash_allowed with the same conditional pattern. The registry.py site was hardened in #2666 with a hasattr(app_config, "sandbox") guard so a SubagentsAppConfig falls back to ambient lookup, but task_tool.py:105 still has the original cfg if cfg is not None else <ambient> pattern and remains vulnerable to the same silent bash-disable bug if ever called with a SubagentsAppConfig.

Fix: teach is_host_bash_allowed itself to fall back to get_app_config() when the passed config lacks .sandbox, and collapse all call sites to a single is_host_bash_allowed(app_config).

Files:

  • backend/packages/harness/deerflow/sandbox/security.py
  • backend/packages/harness/deerflow/subagents/registry.py
  • backend/packages/harness/deerflow/tools/builtins/task_tool.py

2. Single source of truth for ACP agents

AppConfig.acp_agents (Pydantic field, added in #2666) and _acp_agents (module singleton populated by load_acp_config_from_dict) parse the same yaml dict independently. They are equivalent today, but the two paths will drift the moment load_acp_config_from_dict adds any transformation (e.g., $VAR env-variable interpolation already documented on ACPAgentConfig.env).

Fix: either (a) make AppConfig.from_file re-derive the field from the singleton after load_acp_config_from_dict, or (b) drop the singleton and route all reads through app_config.acp_agents.

Files:

  • backend/packages/harness/deerflow/config/app_config.py
  • backend/packages/harness/deerflow/config/acp_config.py
  • backend/packages/harness/deerflow/agents/lead_agent/prompt.py (_build_acp_section)

3. Type-narrow _resolve_subagents_app_config

Currently typed Any | None and uses getattr(app_config, "subagents", app_config) to accept either an AppConfig or a SubagentsAppConfig. End-to-end type flow is broken — any object with a .subagents attribute is silently accepted.

Fix: narrow to AppConfig | SubagentsAppConfig | None with explicit isinstance dispatch.

Files:

  • backend/packages/harness/deerflow/subagents/registry.py

4. Flatten conditional kwarg assembly in task_tool.py

task_tool.py builds available_tools_kwargs and executor_kwargs as dicts, then conditionally adds app_config only when non-None — five sites total (incl. _build_subagent_section in prompt.py). All target signatures (get_available_tools, get_subagent_config, is_host_bash_allowed, SubagentExecutor) already accept app_config=None, so this can be flattened to direct kwarg passing.

Files:

  • backend/packages/harness/deerflow/tools/builtins/task_tool.py
  • backend/packages/harness/deerflow/agents/lead_agent/prompt.py

5. Type the runtime context

runtime/runs/worker.py currently uses Runtime(context=cast(Any, runtime_ctx), store=store) — the cast escapes the type system. The shape is {thread_id: str, run_id: str, app_config?: AppConfig, agent_name?: str} plus arbitrary caller-passthrough keys.

Fix: introduce a DeerFlowRuntimeContext TypedDict, which also lets _get_runtime_app_config(runtime) in task_tool.py use static member access instead of isinstance(context, dict) + .get().

Files:

  • backend/packages/harness/deerflow/runtime/runs/worker.py
  • backend/packages/harness/deerflow/tools/builtins/task_tool.py

6. Verify removed try/except in _create_summarization_middleware

#2666 removed the surrounding try/except that previously logged + fell back to a default skills container path on get_app_config() failure. This is correct only if startup is guaranteed to have already loaded AppConfig successfully by the time this factory runs. If not, a config load failure now propagates instead of degrading.

Action: confirm the invariant holds; if not, restore the graceful degrade.

Files:

  • backend/packages/harness/deerflow/agents/lead_agent/agent.py

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions