fix(kanban): isolate delegated children from parent task

This commit is contained in:
trkim 2026-07-16 21:33:36 +00:00 committed by Teknium
parent a553bc74e4
commit 47bbc12e18
7 changed files with 457 additions and 48 deletions

View file

@ -780,6 +780,7 @@ def _strip_blocked_tools(toolsets: List[str]) -> List[str]:
if name in _COMPOSITE_BLOCKED_TOOLSETS
or all(t in DELEGATE_BLOCKED_TOOLS for t in defn.get("tools", []))
}
blocked_toolset_names.add("kanban")
return [t for t in toolsets if t not in blocked_toolset_names]
@ -1175,7 +1176,7 @@ def _build_child_agent(
]
child_disabled_toolsets = list(
dict.fromkeys(
inherited_disabled + _blocked_toolsets_for_role(effective_role)
inherited_disabled + _blocked_toolsets_for_role(effective_role) + ["kanban"]
)
)
@ -1360,47 +1361,50 @@ def _build_child_agent(
if isinstance(child_max_tokens, int):
child_optional_kwargs["max_tokens"] = child_max_tokens
child = AIAgent(
base_url=effective_base_url,
api_key=effective_api_key,
model=effective_model,
provider=effective_provider,
api_mode=effective_api_mode,
acp_command=effective_acp_command,
acp_args=effective_acp_args,
max_iterations=max_iterations,
from agent.delegation_context import delegated_child_context
reasoning_config=child_reasoning,
prefill_messages=getattr(parent_agent, "prefill_messages", None),
fallback_model=parent_fallback,
enabled_toolsets=child_toolsets,
disabled_toolsets=child_disabled_toolsets,
quiet_mode=True,
ephemeral_system_prompt=child_prompt,
log_prefix=f"[subagent-{task_index}]",
platform="subagent",
skip_context_files=True,
skip_memory=True,
clarify_callback=None,
thinking_callback=child_thinking_cb,
session_db=getattr(parent_agent, "_session_db", None),
parent_session_id=getattr(parent_agent, "session_id", None),
providers_allowed=child_providers_allowed,
providers_ignored=child_providers_ignored,
providers_order=child_providers_order,
provider_sort=child_provider_sort,
provider_require_parameters=child_provider_require_parameters,
provider_data_collection=child_provider_data_collection,
request_overrides=(
dict(override_request_overrides or {})
if override_provider
else dict(getattr(parent_agent, "request_overrides", {}) or {})
),
openrouter_min_coding_score=child_openrouter_min_coding_score,
tool_progress_callback=child_progress_cb,
iteration_budget=None, # fresh budget per subagent
**child_optional_kwargs,
)
with delegated_child_context():
child = AIAgent(
base_url=effective_base_url,
api_key=effective_api_key,
model=effective_model,
provider=effective_provider,
api_mode=effective_api_mode,
acp_command=effective_acp_command,
acp_args=effective_acp_args,
max_iterations=max_iterations,
reasoning_config=child_reasoning,
prefill_messages=getattr(parent_agent, "prefill_messages", None),
fallback_model=parent_fallback,
enabled_toolsets=child_toolsets,
disabled_toolsets=child_disabled_toolsets,
quiet_mode=True,
ephemeral_system_prompt=child_prompt,
log_prefix=f"[subagent-{task_index}]",
platform="subagent",
skip_context_files=True,
skip_memory=True,
clarify_callback=None,
thinking_callback=child_thinking_cb,
session_db=getattr(parent_agent, "_session_db", None),
parent_session_id=getattr(parent_agent, "session_id", None),
providers_allowed=child_providers_allowed,
providers_ignored=child_providers_ignored,
providers_order=child_providers_order,
provider_sort=child_provider_sort,
provider_require_parameters=child_provider_require_parameters,
provider_data_collection=child_provider_data_collection,
request_overrides=(
dict(override_request_overrides or {})
if override_provider
else dict(getattr(parent_agent, "request_overrides", {}) or {})
),
openrouter_min_coding_score=child_openrouter_min_coding_score,
tool_progress_callback=child_progress_cb,
iteration_budget=None, # fresh budget per subagent
**child_optional_kwargs,
)
child._print_fn = getattr(parent_agent, "_print_fn", None)
# Now the child exists, its session id can ride on every relayed event
# (including the spawn_requested below — first emit happens after this).
@ -2001,11 +2005,14 @@ def _run_single_child(
def _run_with_thread_capture():
_worker_thread_holder["t"] = threading.current_thread()
return child.run_conversation(
user_message=goal,
task_id=child_task_id,
stream_callback=_relay_child_text,
)
from agent.delegation_context import delegated_child_context
with delegated_child_context():
return child.run_conversation(
user_message=goal,
task_id=child_task_id,
stream_callback=_relay_child_text,
)
_child_future = _timeout_executor.submit(_run_with_thread_capture)
try: