fix(gateway): bind the session profile once around command.dispatch and bundle routing (#110695)
Follow-up on the salvaged #110698 (which scoped `_dispatch_skill` alone): - One `_session_home_scope(session)` binding around the whole stage loop in `command.dispatch`, so quick commands (`_load_cfg` → `_active_config_path` honours the override), bundles (`skill-bundles/` is home-relative) and skills all resolve against the SAME profile the routing guard used. - `slash.exec` resolves `_bundle_key_for(base)` under the same scope, so a bundle that exists only in the secondary profile is routed to dispatch at all. - `_dispatch_skill` uses the home-keyed `get_skill_commands()` (the guard's reader) instead of an unconditional `scan_skill_commands()`. - Test (red on origin/main): a bundle only under profile B's `skill-bundles/` is dispatched for a session bound to B.
This commit is contained in:
@@ -1222,6 +1222,39 @@ def test_command_dispatch_scopes_skill_lookup_to_session_profile(server, tmp_pat
|
||||
assert resp["result"]["name"] == "b-only"
|
||||
|
||||
|
||||
def test_slash_exec_routes_a_secondary_only_bundle_to_dispatch(server, tmp_path, monkeypatch):
|
||||
"""A skill bundle that exists only under the session profile's ``skill-bundles/`` must be
|
||||
resolved (and routed to command.dispatch) against that profile, not the launch home (#110695)."""
|
||||
import agent.skill_bundles as sb_mod
|
||||
import agent.skill_commands as sc_mod
|
||||
|
||||
monkeypatch.delenv("HERMES_BUNDLES_DIR", raising=False)
|
||||
profile_b = tmp_path / "profile_b"
|
||||
external_b = tmp_path / "external_b"
|
||||
for name in ("one", "two"):
|
||||
d = external_b / name
|
||||
d.mkdir(parents=True)
|
||||
(d / "SKILL.md").write_text(f"---\nname: {name}\ndescription: {name}.\n---\n\n# {name}\n")
|
||||
(profile_b / "skill-bundles").mkdir(parents=True)
|
||||
(profile_b / "skill-bundles" / "b-pack.yaml").write_text("name: b-pack\nskills: [one, two]\n")
|
||||
(profile_b / "config.yaml").write_text(f"skills:\n external_dirs:\n - {external_b}\n")
|
||||
sid = "test-session-profile-b-bundle"
|
||||
server._sessions[sid] = {"session_key": sid, "agent": None, "profile_home": str(profile_b)}
|
||||
|
||||
with (
|
||||
patch("tools.skills_tool.SKILLS_DIR", tmp_path / "no-local-skills"),
|
||||
patch.object(sb_mod, "_bundles_cache", {}),
|
||||
patch.object(sb_mod, "_bundles_cache_mtime", None),
|
||||
patch.object(sc_mod, "_skill_commands", {}),
|
||||
patch.object(sc_mod, "_skill_commands_home", None),
|
||||
):
|
||||
resp = server.handle_request({
|
||||
"id": "r1", "method": "slash.exec", "params": {"command": "/b-pack go", "session_id": sid}})
|
||||
|
||||
assert "error" not in resp, resp
|
||||
assert resp["result"]["type"] == "send" and "b-pack" in resp["result"]["notice"]
|
||||
|
||||
|
||||
def test_command_dispatch_queue_sends_message(server):
|
||||
"""command.dispatch /queue returns {type: 'send', message: ...} for the TUI."""
|
||||
sid = "test-session"
|
||||
|
||||
Reference in New Issue
Block a user