{"repo":"NousResearch/hermes-agent","public_id":"1802","display_label":"delegate_task tool-name state scoping and concurrency bugs","summary":"The overwhelming majority of members concern bugs in delegate_task around _saved_tool_names variable scoping, assignment-before-use errors, and a global _last_resolved_tool_names causing race conditions in concurrent sessions. One unrelated member covers a GitHub Copilot provider integration.","subsystem":"agent","counts":{"members":12,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":4,"merged_prs":2,"attempt_prs":6},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":1894},"resolution":{"state":"partially_resolved","by":"pr-1894,pr-2083"},"freshness":{"state":"stale","last_verified_at":"2026-03-20T01:47:29+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.13757575757575757},"fix_edges":[{"issue_number":1802,"pr_number":1894,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":2079,"pr_number":2083,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":1802,"title":"delegate_task fails: _saved_tool_names not in scope after _build_child_agent/_run_single_child split","url":null,"state":"closed"},{"kind":"issue","number":2027,"title":"fix(delegate): _saved_tool_names used before assignment in _build_child_agent","url":null,"state":"closed"},{"kind":"issue","number":2079,"title":"delegate_task saves child's tool names instead of parent's (post-mutation timing bug)","url":null,"state":"closed"},{"kind":"issue","number":34442,"title":"_last_resolved_tool_names global causes race condition in concurrent sessions","url":null,"state":"closed"},{"kind":"pr","number":1804,"title":"fix: delegate_task NameError — _saved_tool_names scope","url":null,"state":"closed","close_reason":{"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":1864,"title":"fix: restore parent tool-name state after delegated runs","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#1804","number":1804},{"kind":"pr","value":"#1804","number":1804},{"kind":"pr","value":"#1864","number":1864},{"kind":"pr","value":"#1804","number":1804},{"kind":"pr","value":"#1864","number":1864},{"kind":"pr","value":"#1804","number":1804},{"kind":"pr","value":"#1864","number":1864},{"kind":"pr","value":"#1804","number":1804},{"kind":"pr","value":"#1864","number":1864},{"kind":"pr","value":"#1802","number":1802}],"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":1884,"title":"fix(delegate): move _saved_tool_names save/restore to _run_single_child scope","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#1894","number":1894}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":1894,"title":"fix(delegate): move _saved_tool_names save/restore to _run_single_child scope","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["tools/delegate_tool.py"]},{"kind":"pr","number":2028,"title":"fix(delegate): fix _saved_tool_names used before assignment in _build_child_agent","url":null,"state":"closed","close_reason":{"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":2030,"title":"fix(delegate): remove _saved_tool_names used before assignment in _bu…","url":null,"state":"closed","close_reason":{"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":2083,"title":"fix(delegate): save parent tool names before child construction mutates global","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["tools/delegate_tool.py"],"fix_hunks":[{"file":"tools/delegate_tool.py","hunks":["@@ -262,13 +262,11 @@ def _run_single_child(\n     # Get the progress callback from the child agent\n     child_progress_cb = getattr(child, 'tool_progress_callback', None)\n \n-    # Save the parent's resolved tool names before the child agent can\n-    # overwrite the process-global via get_tool_definitions().\n-    # This must be in _run_single_child (not _build_child_agent) so the\n-    # save/restore happens in the same scope as the try/finally.\n+    # Restore parent tool names using the value saved before child construction\n+    # mutated the global. This is the correct parent toolset, not the child's.\n     import model_tools\n-    _saved_tool_names = list(model_tools._last_resolved_tool_names)\n-    child._delegate_saved_tool_names = _saved_tool_names\n+    _saved_tool_names = getattr(child, \"_delegate_saved_tool_names\",\n+                                list(model_tools._last_resolved_tool_names))\n \n     try:\n         result = child.run_conversation(user_message=goal)","@@ -465,6 +463,12 @@ def delegate_task(\n     # Track goal labels for progress display (truncated for readability)\n     task_labels = [t[\"goal\"][:40] for t in task_list]\n \n+    # Save parent tool names BEFORE any child construction mutates the global.\n+    # _build_child_agent() calls AIAgent() which calls get_tool_definitions(),\n+    # which overwrites model_tools._last_resolved_tool_names with child's toolset.\n+    import model_tools as _model_tools\n+    _parent_tool_names = list(_model_tools._last_resolved_tool_names)\n+\n     # Build all child agents on the main thread (thread-safe construction)\n     children = []\n     for i, t in enumerate(task_list):","@@ -476,8 +480,13 @@ def delegate_task(\n             override_api_key=[redacted:secret-assignment]\"api_key\"],\n             override_api_mode=creds[\"api_mode\"],\n         )\n+        # Override with correct parent tool names (before child construction mutated global)\n+        child._delegate_saved_tool_names = _parent_tool_names\n         children.append((i, t, child))\n \n+    # Authoritative restore: reset global to parent's tool names after all children built\n+    _model_tools._last_resolved_tool_names = _parent_tool_names\n+\n     if n_tasks == 1:\n         # Single task -- run directly (no thread pool overhead)\n         _i, _t, child = children[0]\n"]}]},{"kind":"pr","number":34451,"title":"fix(tools): make _last_resolved_tool_names per-context (#34442)","url":null,"state":"closed","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true}],"trust":{"default_tier":"verified","includes_suggested":false}}