fix(agent): accept legacy missing asset placeholders (#39039)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
zyssyz123 2026-07-16 11:11:08 +08:00 committed by GitHub
parent cb41fb3e76
commit 4381ec8fee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 0 deletions

View File

@ -188,6 +188,14 @@ def validate_config_skill_name(name: str) -> str:
return normalized
def _normalize_legacy_missing_asset_file_id(value: Any) -> Any:
"""Canonicalize the null placeholder emitted by early portable Agent DSLs."""
if isinstance(value, dict) and value.get("is_missing") is True and value.get("file_id") is None:
return {**value, "file_id": ""}
return value
class AgentConfigFileRefConfig(BaseModel):
"""Stable Agent Soul reference to one config file payload."""
@ -201,6 +209,11 @@ class AgentConfigFileRefConfig(BaseModel):
hash: str | None = None
mime_type: str | None = None
@model_validator(mode="before")
@classmethod
def _normalize_legacy_file_id(cls, value: Any) -> Any:
return _normalize_legacy_missing_asset_file_id(value)
@field_validator("name")
@classmethod
def _validate_name(cls, value: str) -> str:
@ -231,6 +244,11 @@ class AgentConfigSkillRefConfig(BaseModel):
hash: str | None = None
mime_type: str | None = "application/zip"
@model_validator(mode="before")
@classmethod
def _normalize_legacy_file_id(cls, value: Any) -> Any:
return _normalize_legacy_missing_asset_file_id(value)
@field_validator("name")
@classmethod
def _validate_name(cls, value: str) -> str:

View File

@ -153,6 +153,43 @@ def test_agent_package_round_trips_as_strict_dsl_dto() -> None:
assert restored == package
def test_agent_package_normalizes_legacy_null_missing_asset_file_ids() -> None:
package = make_portable_agent_package(
_agent(),
AgentSoulConfig.model_validate(
{
"config_skills": [{"name": "research", "file_id": "skill-file"}],
"config_files": [{"name": "guide.md", "file_kind": "tool_file", "file_id": "config-file"}],
}
),
).model_dump(mode="json")
package["soul"]["config_skills"][0]["file_id"] = None
package["soul"]["config_files"][0]["file_id"] = None
restored = AgentPackage.model_validate(package)
assert restored.soul.config_skills[0].file_id == ""
assert restored.soul.config_files[0].file_id == ""
assert restored.model_dump(mode="json")["soul"]["config_skills"][0]["file_id"] == ""
assert restored.model_dump(mode="json")["soul"]["config_files"][0]["file_id"] == ""
@pytest.mark.parametrize(
"asset",
[
{"name": "research", "file_id": None, "is_missing": False},
{"name": "guide.md", "file_kind": "tool_file", "file_id": None, "is_missing": False},
],
)
def test_agent_package_rejects_null_file_id_for_available_assets(asset: dict) -> None:
package = make_portable_agent_package(_agent(), AgentSoulConfig()).model_dump(mode="json")
target = "config_files" if "file_kind" in asset else "config_skills"
package["soul"][target] = [asset]
with pytest.raises(ValidationError):
AgentPackage.model_validate(package)
def test_import_warnings_cover_runtime_setup_removed_from_package(monkeypatch) -> None:
soul = AgentSoulConfig.model_validate(
{