feat: agent tools as required tools with tenant tool gate
Agent definitions gain tools (websearch/imagegen/playwright, unknown tool is a 400), stored as agent_tools JSON and returned by GET /tasks. This is deliberately required-tools, not an allowlist: create_thread has no tool allowlist and tenant homes are per-project shared, so blocking other tools cannot be claimed. Instead the worker fails fast before any model call when a declared tool has no [mcp_servers.<tool>] section in the tenant codex-home config.toml (checked after _tenant_dirs seeding, so a section seeded earlier by another worker counts as present). The gate runs only for tenant-isolated Docker workers; local/cloud workers skip it, documented, not verified. Tests: test_agent_tools.py (8 tests).
This commit is contained in:
1 parent
1672ab9ff2
commit
fd37505c48
6 files changed
+383
-7
No files matched your search
+26
-3
@@ -1243,6 +1243,8 @@ def migrate_tasks_table(conn):
|
||||
"agent_name": "TEXT",
|
||||
"agent_version": "TEXT",
|
||||
"agent_instructions": "TEXT",
|
||||
# agent 定义 v3:必需工具 JSON 数组(NULL = 未声明)。
|
||||
"agent_tools": "TEXT",
|
||||
}
|
||||
for name, coltype in new_cols.items():
|
||||
if name not in cols:
|
||||
@@ -1787,6 +1789,20 @@ def _row_get(row, name):
|
||||
return None
|
||||
|
||||
|
||||
def _agent_tools_from_row(row):
|
||||
"""agent_tools 列的容错解析:JSON 数组,非数组/脏数据为 None。"""
|
||||
raw = _row_get(row, "agent_tools")
|
||||
if raw is None:
|
||||
return None
|
||||
if isinstance(raw, list):
|
||||
return list(raw)
|
||||
try:
|
||||
parsed = json.loads(raw)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
return list(parsed) if isinstance(parsed, list) else None
|
||||
|
||||
|
||||
def _task_to_dict(row, attachments=None, total_tokens=None):
|
||||
depends_on = row["depends_on"]
|
||||
try:
|
||||
@@ -1845,6 +1861,9 @@ def _task_to_dict(row, attachments=None, total_tokens=None):
|
||||
"agent_name": _row_get(row, "agent_name"),
|
||||
"agent_version": _row_get(row, "agent_version"),
|
||||
"agent_instructions": _row_get(row, "agent_instructions"),
|
||||
# v3:agent_tools 存 JSON 数组;脏数据/老行缺列时
|
||||
# 按 None 计,不炸列表接口。
|
||||
"agent_tools": _agent_tools_from_row(row),
|
||||
"paused_by_user": _row_get(row, "paused_by_user") or 0,
|
||||
"interrupted_reason": _row_get(row, "interrupted_reason"),
|
||||
"auto_resume_count": _row_get(row, "auto_resume_count") or 0,
|
||||
@@ -4360,6 +4379,7 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
"description": definition.description,
|
||||
"instructions": definition.instructions,
|
||||
"skills": definition.skills,
|
||||
"tools": definition.tools,
|
||||
"defaults": definition.defaults,
|
||||
},
|
||||
known_skills=_agent_known_skills())
|
||||
@@ -4679,6 +4699,8 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
agent_version_val = definition.version if definition else None
|
||||
agent_instructions_val = (
|
||||
definition.instructions if definition else None)
|
||||
agent_tools_val = (
|
||||
json.dumps(definition.tools) if definition else None)
|
||||
now = time.time()
|
||||
conn = self._db()
|
||||
try:
|
||||
@@ -4704,9 +4726,10 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
" environment_id, coordinator_parts,"
|
||||
" parent_task_id, coordinator_part,"
|
||||
" child_model, child_effort,"
|
||||
" agent_name, agent_version, agent_instructions)"
|
||||
" agent_name, agent_version, agent_instructions,"
|
||||
" agent_tools)"
|
||||
" VALUES(?, ?, ?, 'queued', ?, ?, ?, ?, ?, ?, NULL, ?,"
|
||||
" ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
" ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
(task_id, project_id, prompt, idempotency_key,
|
||||
json.dumps(depends_on), priority, user_id, now, now,
|
||||
executor, goal, verify_command, goal_max_rounds,
|
||||
@@ -4715,7 +4738,7 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
json.dumps(coord_parts) if coord_parts else None,
|
||||
None, None, child_model, child_effort,
|
||||
agent_name_val, agent_version_val,
|
||||
agent_instructions_val),
|
||||
agent_instructions_val, agent_tools_val),
|
||||
)
|
||||
# 长任务不限时:API 字段 no_time_limit=1 时写标记,
|
||||
# worker 认领后按不限时跑(单轮 host 600 秒仍在);
|
||||
|
||||
Reference in new issue
Block a user