feat: agent version pinning (v4)
resolve_registry_agent in cloud/agent_def.py: agent_name may be <name> or <name>@<version>, agent_version may also be given separately; both given must agree or 400. Unpinned loads <name>.json (default); pinned loads <name>.<version>.json and requires the file's own version field to match, with no silent fallback to the default file. Version strings are restricted to alphanumerics/._- (no path separators, no ..); the agents-dir traversal guard is a second line of defence. Server: agent_version is only for agent_name (inline agent with agent_version is 400); stored agent_name is the base name, not the @ form. Snapshot fixture: agents/research-brief.v1.json (same content as the default v1 file). Tests: test_agent_version.py (7 tests).
This commit is contained in:
1 parent
8fe4ad2f38
commit
7f60f12639
4 files changed
+277
-31
No files matched your search
+22
-31
@@ -59,8 +59,8 @@ from urllib.parse import parse_qs, quote, urlparse
|
||||
|
||||
from cloud.agent_def import default_agents_dir as _agent_default_dir
|
||||
from cloud.agent_def import list_known_skills as _agent_known_skills
|
||||
from cloud.agent_def import load_agent_definition as _load_agent_definition
|
||||
from cloud.agent_def import parse_agent_definition as _parse_agent_definition
|
||||
from cloud.agent_def import resolve_registry_agent as _resolve_agent
|
||||
from cloud.auth import ROLES as _MEMBER_ROLES
|
||||
from cloud.auth import AuthStore
|
||||
from cloud.coordinator import parse_parts as _coord_parse_parts
|
||||
@@ -4409,18 +4409,30 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
return
|
||||
if prompt_goal_target:
|
||||
prompt = prompt_goal_target
|
||||
# 声明式 agent 定义 v1/v2(见 cloud/agent_def.py):
|
||||
# 声明式 agent 定义 v1/v2/v3(见 cloud/agent_def.py):
|
||||
# inline agent 对象,或 agent_name(注册表 agents/ 下
|
||||
# 的 <name>.json);两者不能同时给,歧义不猜。显式
|
||||
# body 字段逐项优先于定义;解析出的字段落进已有列,
|
||||
# 定义身份与指令落进 agent_name/agent_version/
|
||||
# agent_instructions 列(v2),prompt 不再带头。
|
||||
# 的 <name>.json,未钉选时即默认文件);两者不能同时
|
||||
# 给,歧义不猜。版本钉选(v4):agent_version 字段,
|
||||
# 或 agent_name 里的 <name>@<version> 写法,两者同给
|
||||
# 时必须一致;钉选载 <name>.<version>.json 且文件内
|
||||
# version 必须与请求一致,缺文件绝不回落默认文件。
|
||||
# inline agent 自带 version,agent_version 只配
|
||||
# agent_name用,两者同给即 400。显式 body 字段逐项
|
||||
# 优先于定义;解析出的字段落进已有列,定义身份与
|
||||
# 指令落进 agent_name/agent_version/agent_instructions
|
||||
# 列(v2),prompt 不再带头。
|
||||
definition = None
|
||||
if body.get("agent") is not None \
|
||||
and body.get("agent_name") is not None:
|
||||
self._send_json(400, {
|
||||
"error": "agent and agent_name cannot both be used"})
|
||||
return
|
||||
if body.get("agent") is not None \
|
||||
and body.get("agent_version") is not None:
|
||||
self._send_json(400, {
|
||||
"error": "agent_version is only for agent_name;"
|
||||
" an inline agent carries its own version"})
|
||||
return
|
||||
if body.get("agent") is not None:
|
||||
if not isinstance(body.get("agent"), dict):
|
||||
self._send_json(400, {"error": "agent must be an object"})
|
||||
@@ -4433,32 +4445,11 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
self._send_json(400, {"error": str(exc)})
|
||||
return
|
||||
if body.get("agent_name") is not None:
|
||||
agent_name = body.get("agent_name")
|
||||
if not isinstance(agent_name, str) or not agent_name.strip():
|
||||
self._send_json(400, {
|
||||
"error": "agent_name must be a non-empty string"})
|
||||
return
|
||||
try:
|
||||
definition = _load_agent_definition(
|
||||
agent_name.strip() + ".json",
|
||||
agents_dir=_agent_default_dir())
|
||||
except ValueError as exc:
|
||||
self._send_json(400, {"error": str(exc)})
|
||||
return
|
||||
# 注册表定义同样须过技能包校验:load 只校验了
|
||||
# 格式(known_skills=None 时不查存在性),这里
|
||||
# 用同一解析器补存在性校验,校验不过即 400。
|
||||
try:
|
||||
definition = _parse_agent_definition(
|
||||
{
|
||||
"name": definition.name,
|
||||
"version": definition.version,
|
||||
"description": definition.description,
|
||||
"instructions": definition.instructions,
|
||||
"skills": definition.skills,
|
||||
"tools": definition.tools,
|
||||
"defaults": definition.defaults,
|
||||
},
|
||||
definition = _resolve_agent(
|
||||
body.get("agent_name"),
|
||||
version=body.get("agent_version"),
|
||||
agents_dir=_agent_default_dir(),
|
||||
known_skills=_agent_known_skills())
|
||||
except ValueError as exc:
|
||||
self._send_json(400, {"error": str(exc)})
|
||||
|
||||
Reference in new issue
Block a user