mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-21 02:05:45 +00:00
fix(agents): refuse empty SOUL.md updates in update_agent (#4219)
* fix(agents): refuse empty SOUL.md updates in update_agent setup_agent already rejects empty/whitespace soul (#3553). update_agent is the sibling write path and previously reported success while wiping a working SOUL.md. Mirror the same guard before staging. * fix(agents): guide the retry in the empty-SOUL update rejection Append "Omit the soul field if you do not want to change it." to the empty-soul error so the model self-corrects in one step instead of retrying with another null-like value, matching the "No fields provided" sibling message's helpfulness. Both regression tests assert the guidance.
This commit is contained in:
@@ -145,6 +145,13 @@ def update_agent(
|
||||
if soul is None and description is None and skills is None and tool_groups is None and model is None:
|
||||
return _err('No fields provided. Pass at least one of: soul, description, skills, tool_groups, model. Omit unchanged fields instead of passing null-like strings such as "null", "none", or "undefined".')
|
||||
|
||||
# Reject empty / whitespace-only soul before touching the filesystem.
|
||||
# setup_agent already refuses this (#3553 / #3549); update_agent must too,
|
||||
# otherwise a custom agent can report success while wiping a working
|
||||
# SOUL.md and leaving the next turn with an empty personality.
|
||||
if soul is not None and not soul.strip():
|
||||
return _err("soul content is empty; refusing to update agent with an empty SOUL.md. Omit the soul field if you do not want to change it.")
|
||||
|
||||
try:
|
||||
agent_name = validate_agent_name(agent_name_raw)
|
||||
except ValueError as e:
|
||||
|
||||
@@ -262,6 +262,40 @@ def test_update_agent_updates_soul_only(tmp_path, patched_paths):
|
||||
assert "soul" in result.update["messages"][0].content
|
||||
|
||||
|
||||
def test_update_agent_rejects_empty_soul_and_does_not_overwrite(tmp_path, patched_paths):
|
||||
"""Mirror setup_agent's empty-SOUL guard (#3553 / #3549).
|
||||
|
||||
setup_agent refuses empty/whitespace soul before touching the filesystem.
|
||||
update_agent previously accepted the same input and reported success while
|
||||
writing a blank SOUL.md, wiping a working agent personality.
|
||||
"""
|
||||
agent_dir = _seed_agent(tmp_path, description="keep me", soul="original soul")
|
||||
|
||||
result = update_agent.func(runtime=_runtime(), soul="")
|
||||
|
||||
msg = result.update["messages"][0]
|
||||
assert "soul content is empty" in msg.content
|
||||
# Message must guide the retry (omit the field) so the model self-corrects
|
||||
# in one step instead of retrying with another empty-ish value.
|
||||
assert "Omit the soul field" in msg.content
|
||||
assert msg.status == "error"
|
||||
assert (agent_dir / "SOUL.md").read_text() == "original soul"
|
||||
cfg = yaml.safe_load((agent_dir / "config.yaml").read_text())
|
||||
assert cfg["description"] == "keep me", "config must be untouched on empty-soul reject"
|
||||
|
||||
|
||||
def test_update_agent_rejects_whitespace_only_soul_and_does_not_overwrite(tmp_path, patched_paths):
|
||||
agent_dir = _seed_agent(tmp_path, description="keep me", soul="original soul")
|
||||
|
||||
result = update_agent.func(runtime=_runtime(), soul=" \n\t ")
|
||||
|
||||
msg = result.update["messages"][0]
|
||||
assert "soul content is empty" in msg.content
|
||||
assert "Omit the soul field" in msg.content
|
||||
assert msg.status == "error"
|
||||
assert (agent_dir / "SOUL.md").read_text() == "original soul"
|
||||
|
||||
|
||||
def test_update_agent_updates_description_only(tmp_path, patched_paths):
|
||||
agent_dir = _seed_agent(tmp_path, description="old desc", soul="keep this soul")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user