mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
fix(kanban): reject toolset names in task skills
This commit is contained in:
parent
a91e5a8759
commit
673418dfa1
4 changed files with 39 additions and 0 deletions
|
|
@ -83,6 +83,8 @@ from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Iterable, Optional
|
from typing import Any, Iterable, Optional
|
||||||
|
|
||||||
|
from toolsets import get_toolset_names
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Constants
|
# Constants
|
||||||
|
|
@ -90,6 +92,7 @@ from typing import Any, Iterable, Optional
|
||||||
|
|
||||||
VALID_STATUSES = {"triage", "todo", "ready", "running", "blocked", "done", "archived"}
|
VALID_STATUSES = {"triage", "todo", "ready", "running", "blocked", "done", "archived"}
|
||||||
VALID_WORKSPACE_KINDS = {"scratch", "worktree", "dir"}
|
VALID_WORKSPACE_KINDS = {"scratch", "worktree", "dir"}
|
||||||
|
KNOWN_TOOLSET_NAMES = frozenset(name.casefold() for name in get_toolset_names())
|
||||||
|
|
||||||
# A running task's claim is valid for 15 minutes; after that the next
|
# A running task's claim is valid for 15 minutes; after that the next
|
||||||
# dispatcher tick reclaims it. Workers that outlive this window should call
|
# dispatcher tick reclaims it. Workers that outlive this window should call
|
||||||
|
|
@ -1283,6 +1286,11 @@ def create_task(
|
||||||
f"skill name cannot contain comma: {name!r} "
|
f"skill name cannot contain comma: {name!r} "
|
||||||
f"(pass a list of separate names instead of a comma-joined string)"
|
f"(pass a list of separate names instead of a comma-joined string)"
|
||||||
)
|
)
|
||||||
|
if name.casefold() in KNOWN_TOOLSET_NAMES:
|
||||||
|
raise ValueError(
|
||||||
|
f"{name!r} is a toolset name, not a skill name. "
|
||||||
|
"Put it in the assignee profile's toolsets instead of task skills."
|
||||||
|
)
|
||||||
if name in seen:
|
if name in seen:
|
||||||
continue
|
continue
|
||||||
seen.add(name)
|
seen.add(name)
|
||||||
|
|
|
||||||
|
|
@ -2691,6 +2691,21 @@ def test_create_task_skills_rejects_comma_embedded(kanban_home):
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_task_skills_rejects_toolset_names(kanban_home):
|
||||||
|
"""Toolset names belong in profile config, not per-task skills."""
|
||||||
|
conn = kb.connect()
|
||||||
|
try:
|
||||||
|
with pytest.raises(ValueError, match="toolset name"):
|
||||||
|
kb.create_task(
|
||||||
|
conn,
|
||||||
|
title="bad toolset skill",
|
||||||
|
assignee="x",
|
||||||
|
skills=["web", "translation"],
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
def test_default_spawn_appends_per_task_skills(kanban_home, monkeypatch):
|
def test_default_spawn_appends_per_task_skills(kanban_home, monkeypatch):
|
||||||
"""Dispatcher argv must carry one `--skills X` pair per task skill,
|
"""Dispatcher argv must carry one `--skills X` pair per task skill,
|
||||||
in addition to the built-in kanban-worker."""
|
in addition to the built-in kanban-worker."""
|
||||||
|
|
|
||||||
|
|
@ -1036,6 +1036,20 @@ def test_create_task_without_skills_defaults_to_empty_list(client):
|
||||||
assert task.get("skills") in (None, [])
|
assert task.get("skills") in (None, [])
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_task_with_toolset_name_in_skills_is_rejected(client):
|
||||||
|
"""POST /tasks fails fast when callers confuse toolsets with skills."""
|
||||||
|
r = client.post(
|
||||||
|
"/api/plugins/kanban/tasks",
|
||||||
|
json={
|
||||||
|
"title": "bad skills payload",
|
||||||
|
"assignee": "linguist",
|
||||||
|
"skills": ["web"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert r.status_code == 400, r.text
|
||||||
|
assert "toolset name" in r.json()["detail"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Dispatcher-presence warning in POST /tasks response
|
# Dispatcher-presence warning in POST /tasks response
|
||||||
|
|
|
||||||
|
|
@ -611,6 +611,8 @@ def _handle_create(args: dict, **kw) -> str:
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
except ValueError as e:
|
||||||
|
return tool_error(f"kanban_create: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("kanban_create failed")
|
logger.exception("kanban_create failed")
|
||||||
return tool_error(f"kanban_create: {e}")
|
return tool_error(f"kanban_create: {e}")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue