fix(delegation): forward background flag so delegate_task(background=true) runs async - #46968
Merged
Conversation
Port from Kilo-Org/kilocode#11240. Their issue #11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O.
delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
unresolved-attribute |
2 |
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`
run_agent.py:2920: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`
✅ Fixed issues (1):
| Rule | Count |
|---|---|
invalid-assignment |
1 |
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`
Unchanged: 5771 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
This was referenced Jun 16, 2026
pib-hermes-bot
pushed a commit
to Partners-in-Biz/hermes-agent
that referenced
this pull request
Jun 16, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
alanbratu
pushed a commit
to alanbratu/hermes-agent
that referenced
this pull request
Jun 17, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
zmlgit
pushed a commit
to zmlgit/hermes-agent
that referenced
this pull request
Jun 17, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
al3xar
pushed a commit
to al3xar/hermes-agent
that referenced
this pull request
Jun 17, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
T02200059
pushed a commit
to T02200059/hermes-agent
that referenced
this pull request
Jun 18, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
1 task
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
Methodician
added a commit
to Methodician/hermes-agent
that referenced
this pull request
Jul 4, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
This was referenced Jul 8, 2026
Closed
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
…true) runs async (NousResearch#46968) * fix(skills): guard recursive skill delete against tree-escape Port from Kilo-Org/kilocode#11240. Their issue NousResearch#11227 lost a user's entire working directory: a built-in-skill sentinel location resolved to the server cwd and the skill-removal endpoint ran a recursive delete on it. Hermes' /skills uninstall path (skills_hub.py) is already hardened, but the agent-facing skill_manage(action='delete') path did a bare shutil.rmtree(skill_dir) with no last-line validation. Add _validate_delete_target(): refuse to rmtree a path that (1) isn't strictly inside a known skills root, (2) is a skills root itself, or (3) is reached via a symlink/junction. Tests: 4 cases (normal delete works; symlinked dir, skills-root, out-of-tree all refused). E2E verified with real symlink + file I/O. * fix(delegation): forward background flag in delegate_task dispatch delegate_task is an _AGENT_LOOP_TOOLS member, so every surface (CLI, gateway, desktop/TUI) routes it through AIAgent._dispatch_delegate_task. That forwarder passed every schema field except background, so delegate_task(background=true) was silently downgraded to a synchronous run and returned the sync results payload instead of a delegation_id. The model sees background in the schema (the call validates), but the value never reached the function. Add the one missing kwarg so async background delegation actually engages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
delegate_task(background=true)now actually runs the subagent asynchronously instead of silently blocking. Previously thebackgroundflag was dropped before reaching the function, so it always ran synchronously and returned the sync results payload instead of adelegation_id.Root cause:
delegate_taskis an_AGENT_LOOP_TOOLSmember, so every surface (CLI, gateway, desktop/TUI) routes it through the single chokepointAIAgent._dispatch_delegate_task(run_agent.py). That forwarder passed every schema field exceptbackground. The model seesbackgroundin the schema (the call validates), but the value never reacheddelegate_task, which defaulted it toFalseand ran synchronously. The generic registry handler does forward it, but that path is only a fallback stub for agent-loop tools and is never the live path.Not desktop-specific despite the report framing — it affected every surface.
Changes
run_agent.py: addbackground=function_args.get("background")to_dispatch_delegate_task.Validation
backgroundreachesdelegate_tasktests/tools/test_async_delegation.pyE2E: stubbed
delegate_taskand invoked the real_dispatch_delegate_task({"goal":..., "background": True})— confirmedbackground=Truenow arrives at the function (wasNone/dropped before).Infographic