fix(agents): require config.yaml before treating per-user agent dir as resolved#3209
Open
IECspace wants to merge 1 commit into
Open
fix(agents): require config.yaml before treating per-user agent dir as resolved#3209IECspace wants to merge 1 commit into
IECspace wants to merge 1 commit into
Conversation
…s resolved
`resolve_agent_dir` previously treated any existing
`users/{uid}/agents/{name}/` directory as a valid user-owned agent dir.
However the memory writer creates that directory with just `memory.json`
on the first chat, before any config exists. The next request then
resolves to that dir, `load_agent_config` cannot find `config.yaml`, and
the agent disappears from the assistant menu with
`FileNotFoundError: Agent config not found`.
Guard the per-user fast path with an explicit `config.yaml` existence
check so a memory-only directory falls through to the legacy shared
agents path, matching how `list_custom_agents` already treats
`config.yaml` as the gate.
Adds four regression tests covering:
- memory-only user dir falls through to legacy
- user dir with config.yaml still wins over legacy
- empty filesystem returns per-user write target
- end-to-end load_agent_config succeeds via legacy fallback
Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Background
When a user starts their first chat with a custom agent, the memory writer creates
{base_dir}/users/{uid}/agents/{name}/memory.jsonto persist agent memory.At that point the per-user directory exists, but only
memory.jsonis in it —no
config.yamlis written there because the agent itself still lives underthe legacy shared location
{base_dir}/agents/{name}/.resolve_agent_dircurrently treats the existence ofusers/{uid}/agents/{name}/as sufficient evidence that the user owns the agent:
So on the next request from the same user,
resolve_agent_dirreturns thememory-only path. The downstream
load_agent_configcannot findconfig.yamlthere and raises:
User-visible symptom: the custom agent disappears from the assistant menu after
the user's first successful chat with it. The agent only reappears after manual
intervention (deleting
memory.json, restarting, or seedingconfig.yaml).This was reproduced on a downstream deployment that uses a custom auth
middleware mapping external users to DeerFlow user IDs, but the failure is
intrinsic to upstream
resolve_agent_dirand reproducible on plainbytedance/deer-flowwith any custom agent (including those created via the agents API).
Goal
resolve_agent_dirshould resolve to the per-user directory only when thatdirectory actually contains a config.yaml, otherwise it should fall through
to the legacy shared layout (or to the empty per-user write target if neither
exists).
This keeps the existing semantics for:
…while fixing the case where the memory subsystem has lazily created a per-user
agent dir but no agent config lives there yet.
Solution
Add an explicit
config.yamlexistence check to the per-user fast path:This mirrors the gate that
list_custom_agentsalready applies when scanningdirectories (it skips entries without
config.yaml). Nowresolve_agent_dirisconsistent with that contract.
The docstring is updated to spell out why directory existence alone is not
enough, so the next person who reads the code understands the memory-writer
interaction.
Tests
Added a new
TestResolveAgentDirclass inbackend/tests/test_custom_agent.pycovering:
test_skips_user_dir_with_memory_onlymemory.jsonfalls through to legacytest_prefers_user_dir_when_config_yaml_existsconfig.yamlstill winstest_returns_user_path_when_neither_existstest_load_agent_config_falls_back_when_user_has_memory_onlyload_agent_configsucceeds via legacy fallbackTest results
Run on
python 3.12.13,pytest 9.0.3,uvmanaged environment.With this patch:
Without this patch (reverting just the 1-line change, tests kept):
i.e. the two new tests that target the bug reliably reproduce the original
production symptom, and pass once the guard is added.
No regression in nearby modules:
Total: 125 passed across
test_custom_agent.pyand the four most directlyrelated test modules.
Lint / format
Compatibility
which is silently steered back onto the legacy fallback.
Made with Cursor