feat: version platform agent profiles through main agent runs
This commit is contained in:
23
CHANGELOG.md
23
CHANGELOG.md
@@ -118,6 +118,29 @@
|
||||
- 当前基线重新验证通过:
|
||||
- 前端测试 `67/67`
|
||||
- 后端单测 `36/36`
|
||||
|
||||
## 2026-04-04
|
||||
|
||||
### 平台 Agent 配置历史与回滚
|
||||
|
||||
- `平台 Agent 配置` 现在和 `OneLiner 主配置` 一样,已经支持版本历史、回滚和审计,不再只是直接编辑当前值。
|
||||
- 后端新增了:
|
||||
- `GET /v2/platform-agents/{platform}/profile/versions`
|
||||
- `GET /v2/platform-agents/{platform}/profile/audits`
|
||||
- `POST /v2/platform-agents/{platform}/profile/rollback`
|
||||
- `PUT /v2/platform-agents/{platform}/profile` 现在支持记录变更原因,并在保存时自动生成新的版本快照。
|
||||
- 前端 `平台 Agent 配置` 弹层新增当前版本摘要和变更原因,`平台 Agent 面板 / 详情` 也都新增了 `看配置历史` 入口。
|
||||
|
||||
### 平台 Agent 配置进入执行回写
|
||||
|
||||
- 主 Agent 在创建 run、重试 run、完成 run 时,都会把当前平台 Agent 配置版本号一起带入执行链。
|
||||
- 平台 Agent 的 `recent_execution` 现在会显示本轮使用的 `平台 Agent vN`,方便直接判断最近一次执行到底用了哪版平台配置。
|
||||
- run 完成态结果里的 `execution_card.platform_agent_profile` 也会携带平台 Agent 版本号、标题和摘要,悬浮主 Agent 结果卡能直接回看这轮平台配置来源。
|
||||
|
||||
### 回归护栏
|
||||
|
||||
- 后端治理测试新增了平台 Agent 配置版本链路覆盖:初始化版本、连续更新、回滚生成新版本、审计记录,以及执行完成后的 `recent_execution.platform_agent_profile_version_no` 回写。
|
||||
- 前端工作台测试新增了平台 Agent 配置历史入口、历史接口、回滚接口和结果卡版本显示的校验,避免后续再把这条链断开。
|
||||
- `bash scripts/check_repo_baseline.sh`
|
||||
- `bash scripts/smoke_fnos_storyforge_lan.sh`
|
||||
|
||||
|
||||
@@ -370,6 +370,7 @@ class Database:
|
||||
"last_used_at": "TEXT NOT NULL DEFAULT ''",
|
||||
"last_intent_key": "TEXT NOT NULL DEFAULT ''",
|
||||
"last_oneliner_profile_version_no": "INTEGER NOT NULL DEFAULT 0",
|
||||
"last_platform_profile_version_no": "INTEGER NOT NULL DEFAULT 0",
|
||||
"last_execution_summary": "TEXT NOT NULL DEFAULT ''",
|
||||
"last_source_screen": "TEXT NOT NULL DEFAULT ''",
|
||||
},
|
||||
|
||||
@@ -49,6 +49,13 @@ class PlatformAgentProfileRequest(BaseModel):
|
||||
notes: str = ""
|
||||
status: str = "active"
|
||||
config: dict[str, Any] = Field(default_factory=dict)
|
||||
reason: str = ""
|
||||
|
||||
|
||||
class PlatformAgentProfileRollbackRequest(BaseModel):
|
||||
project_id: str = ""
|
||||
version_id: str
|
||||
reason: str = ""
|
||||
|
||||
|
||||
class AgentMemoryUpsertRequest(BaseModel):
|
||||
@@ -510,6 +517,45 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
FOREIGN KEY(assistant_id) REFERENCES assistants(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_agent_profile_versions (
|
||||
id TEXT PRIMARY KEY,
|
||||
profile_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
project_id TEXT NOT NULL DEFAULT '',
|
||||
platform TEXT NOT NULL DEFAULT '',
|
||||
assistant_id TEXT NOT NULL DEFAULT '',
|
||||
version_no INTEGER NOT NULL DEFAULT 1,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
mission TEXT NOT NULL DEFAULT '',
|
||||
notes TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
config_json TEXT NOT NULL DEFAULT '{}',
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
reason TEXT NOT NULL DEFAULT '',
|
||||
source_type TEXT NOT NULL DEFAULT 'user_update',
|
||||
rollback_from_version_id TEXT NOT NULL DEFAULT '',
|
||||
actor_user_id TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
UNIQUE(profile_id, version_no),
|
||||
FOREIGN KEY(profile_id) REFERENCES platform_agent_profiles(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES accounts(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY(assistant_id) REFERENCES assistants(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_agent_profile_audit_logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
profile_id TEXT NOT NULL,
|
||||
version_id TEXT NOT NULL DEFAULT '',
|
||||
actor_user_id TEXT NOT NULL DEFAULT '',
|
||||
action_key TEXT NOT NULL DEFAULT '',
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
details_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY(profile_id) REFERENCES platform_agent_profiles(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(version_id) REFERENCES platform_agent_profile_versions(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agent_memories (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
@@ -1064,6 +1110,217 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
)
|
||||
return payload
|
||||
|
||||
def _summarize_platform_agent_profile(row: dict[str, Any] | None) -> str:
|
||||
data = row or {}
|
||||
def _clip(value: str, limit: int) -> str:
|
||||
text = str(value or "").strip()
|
||||
if len(text) <= limit:
|
||||
return text
|
||||
return f"{text[: max(limit - 1, 0)].rstrip()}…"
|
||||
parts: list[str] = []
|
||||
name = str(data.get("name") or "").strip()
|
||||
if name:
|
||||
parts.append(name)
|
||||
mission = str(data.get("mission") or "").strip()
|
||||
if mission:
|
||||
parts.append(_clip(mission, 36))
|
||||
notes = str(data.get("notes") or "").strip()
|
||||
if notes:
|
||||
parts.append(f"备注 {_clip(notes, 32)}")
|
||||
return ",".join(parts[:4])
|
||||
|
||||
def _platform_agent_profile_version_payload(row: dict[str, Any] | None) -> dict[str, Any] | None:
|
||||
if not row:
|
||||
return None
|
||||
assistant = None
|
||||
if row.get("assistant_id"):
|
||||
assistant_row = legacy.db.fetch_one("SELECT * FROM assistants WHERE id = ?", (row["assistant_id"],))
|
||||
if assistant_row:
|
||||
assistant = legacy.assistant_payload(assistant_row)
|
||||
return {
|
||||
"id": row["id"],
|
||||
"profile_id": row.get("profile_id", ""),
|
||||
"user_id": row.get("user_id", ""),
|
||||
"project_id": row.get("project_id", ""),
|
||||
"platform": row.get("platform", ""),
|
||||
"assistant_id": row.get("assistant_id", ""),
|
||||
"version_no": int(row.get("version_no") or 0),
|
||||
"name": row.get("name", ""),
|
||||
"mission": row.get("mission", ""),
|
||||
"notes": row.get("notes", ""),
|
||||
"status": row.get("status", "active"),
|
||||
"config": _parse_json(row.get("config_json"), {}),
|
||||
"summary": row.get("summary", ""),
|
||||
"reason": row.get("reason", ""),
|
||||
"source_type": row.get("source_type", ""),
|
||||
"rollback_from_version_id": row.get("rollback_from_version_id", ""),
|
||||
"actor_user_id": row.get("actor_user_id", ""),
|
||||
"assistant": assistant,
|
||||
"created_at": row.get("created_at", ""),
|
||||
}
|
||||
|
||||
def _platform_agent_profile_audit_payload(row: dict[str, Any] | None) -> dict[str, Any] | None:
|
||||
if not row:
|
||||
return None
|
||||
version_row = legacy.db.fetch_one("SELECT * FROM platform_agent_profile_versions WHERE id = ?", (row.get("version_id", ""),)) if row.get("version_id") else None
|
||||
return {
|
||||
"id": row["id"],
|
||||
"profile_id": row.get("profile_id", ""),
|
||||
"version_id": row.get("version_id", ""),
|
||||
"actor_user_id": row.get("actor_user_id", ""),
|
||||
"action_key": row.get("action_key", ""),
|
||||
"summary": row.get("summary", ""),
|
||||
"details": _parse_json(row.get("details_json"), {}),
|
||||
"version": _platform_agent_profile_version_payload(version_row),
|
||||
"created_at": row.get("created_at", ""),
|
||||
}
|
||||
|
||||
def _list_platform_agent_profile_versions(profile_row: dict[str, Any] | None) -> list[dict[str, Any]]:
|
||||
if not profile_row:
|
||||
return []
|
||||
rows = legacy.db.fetch_all(
|
||||
"""
|
||||
SELECT * FROM platform_agent_profile_versions
|
||||
WHERE profile_id = ?
|
||||
ORDER BY version_no DESC, created_at DESC
|
||||
""",
|
||||
(profile_row["id"],),
|
||||
)
|
||||
return [_platform_agent_profile_version_payload(row) for row in rows if row]
|
||||
|
||||
def _list_platform_agent_profile_audits(profile_row: dict[str, Any] | None, *, limit: int = 12) -> list[dict[str, Any]]:
|
||||
if not profile_row:
|
||||
return []
|
||||
rows = legacy.db.fetch_all(
|
||||
"""
|
||||
SELECT * FROM platform_agent_profile_audit_logs
|
||||
WHERE profile_id = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(profile_row["id"], limit),
|
||||
)
|
||||
return [_platform_agent_profile_audit_payload(row) for row in rows if row]
|
||||
|
||||
def _current_platform_agent_profile_version_row(profile_row: dict[str, Any] | None) -> dict[str, Any] | None:
|
||||
if not profile_row:
|
||||
return None
|
||||
return legacy.db.fetch_one(
|
||||
"""
|
||||
SELECT * FROM platform_agent_profile_versions
|
||||
WHERE profile_id = ?
|
||||
ORDER BY version_no DESC, created_at DESC
|
||||
LIMIT 1
|
||||
""",
|
||||
(profile_row["id"],),
|
||||
)
|
||||
|
||||
def _log_platform_agent_profile_audit(
|
||||
*,
|
||||
profile_id: str,
|
||||
version_id: str,
|
||||
actor_user_id: str,
|
||||
action_key: str,
|
||||
summary: str,
|
||||
details: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
audit_id = make_id("plat_agent_audit")
|
||||
legacy.db.execute(
|
||||
"""
|
||||
INSERT INTO platform_agent_profile_audit_logs (
|
||||
id, profile_id, version_id, actor_user_id, action_key, summary, details_json, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
audit_id,
|
||||
profile_id,
|
||||
version_id,
|
||||
actor_user_id,
|
||||
action_key,
|
||||
summary,
|
||||
_dump(details or {}),
|
||||
now(),
|
||||
),
|
||||
)
|
||||
row = legacy.db.fetch_one("SELECT * FROM platform_agent_profile_audit_logs WHERE id = ?", (audit_id,))
|
||||
assert row is not None
|
||||
return _platform_agent_profile_audit_payload(row)
|
||||
|
||||
def _create_platform_agent_profile_version(
|
||||
profile_row: dict[str, Any],
|
||||
*,
|
||||
actor_user_id: str,
|
||||
source_type: str,
|
||||
reason: str,
|
||||
rollback_from_version_id: str = "",
|
||||
) -> dict[str, Any]:
|
||||
current = legacy.db.fetch_one(
|
||||
"SELECT COALESCE(MAX(version_no), 0) AS max_version FROM platform_agent_profile_versions WHERE profile_id = ?",
|
||||
(profile_row["id"],),
|
||||
)
|
||||
version_no = int((current or {}).get("max_version") or 0) + 1
|
||||
version_id = make_id("plat_agent_ver")
|
||||
summary = _summarize_platform_agent_profile(profile_row)
|
||||
version_params = (
|
||||
version_id,
|
||||
profile_row["id"],
|
||||
profile_row.get("user_id", ""),
|
||||
profile_row.get("project_id", ""),
|
||||
profile_row.get("platform", ""),
|
||||
profile_row.get("assistant_id", ""),
|
||||
version_no,
|
||||
profile_row.get("name", ""),
|
||||
profile_row.get("mission", ""),
|
||||
profile_row.get("notes", ""),
|
||||
profile_row.get("status", "active"),
|
||||
profile_row.get("config_json", "{}"),
|
||||
summary,
|
||||
reason.strip(),
|
||||
source_type,
|
||||
rollback_from_version_id,
|
||||
actor_user_id,
|
||||
now(),
|
||||
)
|
||||
insert_sql = """
|
||||
INSERT INTO platform_agent_profile_versions (
|
||||
id, profile_id, user_id, project_id, platform, assistant_id, version_no,
|
||||
name, mission, notes, status, config_json, summary, reason,
|
||||
source_type, rollback_from_version_id, actor_user_id, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
if profile_row.get("assistant_id"):
|
||||
legacy.db.execute(insert_sql, version_params)
|
||||
else:
|
||||
with legacy.db.session() as conn:
|
||||
conn.execute("PRAGMA foreign_keys=OFF")
|
||||
conn.execute(insert_sql, version_params)
|
||||
conn.execute("PRAGMA foreign_keys=ON")
|
||||
row = legacy.db.fetch_one("SELECT * FROM platform_agent_profile_versions WHERE id = ?", (version_id,))
|
||||
assert row is not None
|
||||
return _platform_agent_profile_version_payload(row)
|
||||
|
||||
def _ensure_platform_agent_profile_version_seed(profile_row: dict[str, Any], *, actor_user_id: str = "") -> dict[str, Any]:
|
||||
current_version = _current_platform_agent_profile_version_row(profile_row)
|
||||
if current_version:
|
||||
payload = _platform_agent_profile_version_payload(current_version)
|
||||
assert payload is not None
|
||||
return payload
|
||||
payload = _create_platform_agent_profile_version(
|
||||
profile_row,
|
||||
actor_user_id=actor_user_id or profile_row.get("user_id", ""),
|
||||
source_type="system_seed",
|
||||
reason="初始化平台 Agent 配置",
|
||||
)
|
||||
_log_platform_agent_profile_audit(
|
||||
profile_id=profile_row["id"],
|
||||
version_id=payload["id"],
|
||||
actor_user_id=actor_user_id or profile_row.get("user_id", ""),
|
||||
action_key="seed-platform-agent-profile",
|
||||
summary="初始化平台 Agent 配置版本",
|
||||
details={"project_id": profile_row.get("project_id", ""), "platform": profile_row.get("platform", "")},
|
||||
)
|
||||
return payload
|
||||
|
||||
def _oneliner_profile_runtime_snapshot(row: dict[str, Any], *, account: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
bundle = _oneliner_profile_bundle(row, account=account)
|
||||
return {
|
||||
@@ -1226,6 +1483,10 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
else:
|
||||
readiness_label = "待补全"
|
||||
recent_execution = None
|
||||
current_version = None
|
||||
if row:
|
||||
_ensure_platform_agent_profile_version_seed(row, actor_user_id=account.get("id", ""))
|
||||
current_version = _platform_agent_profile_version_payload(_current_platform_agent_profile_version_row(row))
|
||||
if row and str(row.get("last_run_id") or "").strip():
|
||||
recent_execution = {
|
||||
"run_id": str(row.get("last_run_id") or "").strip(),
|
||||
@@ -1234,6 +1495,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
"intent_key": str(row.get("last_intent_key") or "").strip(),
|
||||
"intent_label": INTENT_LABELS.get(str(row.get("last_intent_key") or "").strip() or "custom", "主 Agent 任务"),
|
||||
"oneliner_profile_version_no": int(row.get("last_oneliner_profile_version_no") or 0),
|
||||
"platform_agent_profile_version_no": int(row.get("last_platform_profile_version_no") or 0),
|
||||
"summary": str(row.get("last_execution_summary") or "").strip(),
|
||||
"source_screen": str(row.get("last_source_screen") or "").strip(),
|
||||
}
|
||||
@@ -1262,6 +1524,11 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
"intent_key": str(latest_run_row.get("intent_key") or "").strip(),
|
||||
"intent_label": INTENT_LABELS.get(str(latest_run_row.get("intent_key") or "").strip() or "custom", "主 Agent 任务"),
|
||||
"oneliner_profile_version_no": int(latest_profile_version.get("version_no") or 0),
|
||||
"platform_agent_profile_version_no": int(
|
||||
(((latest_result.get("execution_card") or {}).get("platform_agent_profile") or {}).get("version_no"))
|
||||
or (((latest_governance.get("platform_agent_profile") or {}).get("current_version") or {}).get("version_no"))
|
||||
or 0
|
||||
),
|
||||
"summary": str(latest_run_row.get("status_summary") or "").strip(),
|
||||
"source_screen": str(latest_run_row.get("source_screen") or "").strip(),
|
||||
}
|
||||
@@ -1285,6 +1552,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
"readiness_score": readiness_score,
|
||||
"readiness_label": readiness_label,
|
||||
"assistant": assistant,
|
||||
"current_version": current_version,
|
||||
"created_at": (row or {}).get("created_at", ""),
|
||||
"updated_at": (row or {}).get("updated_at", ""),
|
||||
}
|
||||
@@ -1317,6 +1585,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
"recent_memory_title": str(recent_memory.get("title") or "").strip(),
|
||||
"recent_skill_title": str(recent_skill.get("title") or "").strip(),
|
||||
"recent_execution": payload.get("recent_execution") or {},
|
||||
"current_version": payload.get("current_version") or {},
|
||||
}
|
||||
|
||||
def _record_platform_agent_execution_feedback(
|
||||
@@ -1329,6 +1598,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
intent_key: str,
|
||||
source_screen: str,
|
||||
oneliner_profile_version_no: int,
|
||||
platform_agent_profile_version_no: int,
|
||||
execution_summary: str,
|
||||
) -> None:
|
||||
normalized_platform = _safe_platform(platform, fallback="") if str(platform or "").strip() else ""
|
||||
@@ -1338,7 +1608,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
"""
|
||||
UPDATE platform_agent_profiles
|
||||
SET last_run_id = ?, last_run_status = ?, last_used_at = ?, last_intent_key = ?,
|
||||
last_oneliner_profile_version_no = ?, last_execution_summary = ?, last_source_screen = ?, updated_at = ?
|
||||
last_oneliner_profile_version_no = ?, last_platform_profile_version_no = ?, last_execution_summary = ?, last_source_screen = ?, updated_at = ?
|
||||
WHERE user_id = ? AND project_id = ? AND platform = ?
|
||||
""",
|
||||
(
|
||||
@@ -1347,6 +1617,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
now(),
|
||||
str(intent_key or "custom").strip() or "custom",
|
||||
int(oneliner_profile_version_no or 0),
|
||||
int(platform_agent_profile_version_no or 0),
|
||||
str(execution_summary or "").strip(),
|
||||
str(source_screen or "").strip(),
|
||||
now(),
|
||||
@@ -3336,6 +3607,9 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
"platform_label": str(platform_agent_profile.get("platform_label") or "").strip(),
|
||||
"name": str(platform_agent_profile.get("name") or "").strip(),
|
||||
"assistant_name": str(platform_agent_profile.get("assistant_name") or "").strip(),
|
||||
"version_no": int(((platform_agent_profile.get("current_version") or {}).get("version_no") or 0)),
|
||||
"version_title": str(((platform_agent_profile.get("current_version") or {}).get("title") or "").strip()),
|
||||
"version_summary": str(((platform_agent_profile.get("current_version") or {}).get("summary") or "").strip()),
|
||||
"mission": str(platform_agent_profile.get("mission") or "").strip(),
|
||||
"status": str(platform_agent_profile.get("status") or "").strip(),
|
||||
"readiness_label": str(platform_agent_profile.get("readiness_label") or "").strip(),
|
||||
@@ -3373,6 +3647,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
intent_key=str(plan.get("intent_key") or row.get("intent_key") or "custom").strip() or "custom",
|
||||
source_screen=str(row.get("source_screen") or "").strip(),
|
||||
oneliner_profile_version_no=int(oneliner_profile_version.get("version_no") or 0),
|
||||
platform_agent_profile_version_no=int(((platform_agent_profile.get("current_version") or {}).get("version_no") or 0)),
|
||||
execution_summary=execution_summary,
|
||||
)
|
||||
_log_agent_run_event(
|
||||
@@ -4058,6 +4333,21 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
conn.execute(insert_sql, insert_params)
|
||||
conn.execute("PRAGMA foreign_keys=ON")
|
||||
row = legacy.db.fetch_one("SELECT * FROM platform_agent_profiles WHERE id = ?", (profile_id,))
|
||||
assert row is not None
|
||||
version = _create_platform_agent_profile_version(
|
||||
row,
|
||||
actor_user_id=account["id"],
|
||||
source_type="user_update",
|
||||
reason=request.reason.strip() or f"更新 {legacy.platform_label(platform)} Agent 配置",
|
||||
)
|
||||
_log_platform_agent_profile_audit(
|
||||
profile_id=row["id"],
|
||||
version_id=version["id"],
|
||||
actor_user_id=account["id"],
|
||||
action_key="update-platform-agent-profile",
|
||||
summary=f"已更新 {legacy.platform_label(platform)} Agent 配置",
|
||||
details={"project_id": project["id"], "platform": platform, "assistant_id": row.get("assistant_id", "")},
|
||||
)
|
||||
return _platform_agent_payload(account, row, platform=platform, project_id=project["id"])
|
||||
|
||||
def _upsert_memory(
|
||||
@@ -5832,6 +6122,110 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
normalized_platform = _safe_platform(platform)
|
||||
return _upsert_platform_profile(account, normalized_platform, request)
|
||||
|
||||
@app.get("/v2/platform-agents/{platform}/profile/versions")
|
||||
def list_platform_agent_profile_versions(
|
||||
platform: str,
|
||||
project_id: str | None = Query(default=None),
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, project_id or None)
|
||||
normalized_platform = _safe_platform(platform)
|
||||
row = legacy.db.fetch_one(
|
||||
"SELECT * FROM platform_agent_profiles WHERE user_id = ? AND project_id = ? AND platform = ?",
|
||||
(account["id"], project["id"], normalized_platform),
|
||||
)
|
||||
if not row:
|
||||
return {"items": [], "count": 0, "current_version": None}
|
||||
_ensure_platform_agent_profile_version_seed(row, actor_user_id=account["id"])
|
||||
items = _list_platform_agent_profile_versions(row)
|
||||
return {"items": items, "count": len(items), "current_version": items[0] if items else None}
|
||||
|
||||
@app.get("/v2/platform-agents/{platform}/profile/audits")
|
||||
def list_platform_agent_profile_audits(
|
||||
platform: str,
|
||||
project_id: str | None = Query(default=None),
|
||||
limit: int = Query(default=12, ge=1, le=50),
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, project_id or None)
|
||||
normalized_platform = _safe_platform(platform)
|
||||
row = legacy.db.fetch_one(
|
||||
"SELECT * FROM platform_agent_profiles WHERE user_id = ? AND project_id = ? AND platform = ?",
|
||||
(account["id"], project["id"], normalized_platform),
|
||||
)
|
||||
if not row:
|
||||
return {"items": [], "count": 0}
|
||||
_ensure_platform_agent_profile_version_seed(row, actor_user_id=account["id"])
|
||||
items = _list_platform_agent_profile_audits(row, limit=limit)
|
||||
return {"items": items, "count": len(items)}
|
||||
|
||||
@app.post("/v2/platform-agents/{platform}/profile/rollback")
|
||||
def rollback_platform_agent_profile(
|
||||
platform: str,
|
||||
request: PlatformAgentProfileRollbackRequest,
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, request.project_id or None)
|
||||
normalized_platform = _safe_platform(platform)
|
||||
row = legacy.db.fetch_one(
|
||||
"SELECT * FROM platform_agent_profiles WHERE user_id = ? AND project_id = ? AND platform = ?",
|
||||
(account["id"], project["id"], normalized_platform),
|
||||
)
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="Platform agent profile not found")
|
||||
_ensure_platform_agent_profile_version_seed(row, actor_user_id=account["id"])
|
||||
target_version = legacy.db.fetch_one(
|
||||
"""
|
||||
SELECT * FROM platform_agent_profile_versions
|
||||
WHERE id = ? AND profile_id = ?
|
||||
""",
|
||||
(request.version_id, row["id"]),
|
||||
)
|
||||
if not target_version:
|
||||
raise HTTPException(status_code=404, detail="Platform agent profile version not found")
|
||||
assistant = _resolve_assistant(account, target_version.get("assistant_id", ""), project["id"])
|
||||
timestamp = now()
|
||||
update_sql = """
|
||||
UPDATE platform_agent_profiles
|
||||
SET assistant_id = ?, name = ?, mission = ?, notes = ?, status = ?, config_json = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
"""
|
||||
update_params = (
|
||||
(assistant or {}).get("id", ""),
|
||||
target_version.get("name", f"{legacy.platform_label(normalized_platform)} Agent"),
|
||||
target_version.get("mission", ""),
|
||||
target_version.get("notes", ""),
|
||||
target_version.get("status", "active"),
|
||||
target_version.get("config_json", "{}"),
|
||||
timestamp,
|
||||
row["id"],
|
||||
)
|
||||
if (assistant or {}).get("id", ""):
|
||||
legacy.db.execute(update_sql, update_params)
|
||||
else:
|
||||
with legacy.db.session() as conn:
|
||||
conn.execute("PRAGMA foreign_keys=OFF")
|
||||
conn.execute(update_sql, update_params)
|
||||
conn.execute("PRAGMA foreign_keys=ON")
|
||||
updated_row = legacy.db.fetch_one("SELECT * FROM platform_agent_profiles WHERE id = ?", (row["id"],))
|
||||
assert updated_row is not None
|
||||
version = _create_platform_agent_profile_version(
|
||||
updated_row,
|
||||
actor_user_id=account["id"],
|
||||
source_type="user_rollback",
|
||||
reason=request.reason.strip() or f"回滚到版本 {target_version.get('version_no') or request.version_id}",
|
||||
rollback_from_version_id=target_version["id"],
|
||||
)
|
||||
_log_platform_agent_profile_audit(
|
||||
profile_id=updated_row["id"],
|
||||
version_id=version["id"],
|
||||
actor_user_id=account["id"],
|
||||
action_key="rollback-platform-agent-profile",
|
||||
summary=f"已回滚 {legacy.platform_label(normalized_platform)} Agent 配置到版本 {target_version.get('version_no') or request.version_id}",
|
||||
details={"project_id": project["id"], "platform": normalized_platform, "rollback_to_version_id": target_version["id"]},
|
||||
)
|
||||
return _platform_agent_payload(account, updated_row, platform=normalized_platform, project_id=project["id"])
|
||||
|
||||
@app.get("/v2/platform-agents/{platform}/memories")
|
||||
def list_platform_memories(
|
||||
platform: str,
|
||||
|
||||
@@ -721,6 +721,16 @@ class MainAgentGovernanceTests(unittest.TestCase):
|
||||
self.assertEqual(profile_payload["platform"], "douyin")
|
||||
self.assertEqual(profile_payload["name"], "抖音增长 Agent")
|
||||
self.assertEqual(profile_payload["config"]["focus"], "conversion")
|
||||
self.assertEqual(profile_payload["current_version"]["version_no"], 1)
|
||||
first_profile_version_id = profile_payload["current_version"]["id"]
|
||||
|
||||
profile_versions = self.client.get(
|
||||
"/v2/platform-agents/douyin/profile/versions",
|
||||
headers=self.ctx["member_headers"],
|
||||
params={"project_id": self.ctx["project_id"]},
|
||||
)
|
||||
self.assertEqual(profile_versions.status_code, 200, profile_versions.text)
|
||||
self.assertEqual(profile_versions.json()["count"], 1)
|
||||
|
||||
memory_response = self.client.post(
|
||||
"/v2/platform-agents/douyin/memories",
|
||||
@@ -790,6 +800,44 @@ class MainAgentGovernanceTests(unittest.TestCase):
|
||||
self.assertEqual(review_skill.status_code, 200, review_skill.text)
|
||||
self.assertEqual(review_skill.json()["status"], "validated")
|
||||
|
||||
update_profile = self.client.put(
|
||||
"/v2/platform-agents/douyin/profile",
|
||||
headers=self.ctx["member_headers"],
|
||||
json={
|
||||
"project_id": self.ctx["project_id"],
|
||||
"name": "抖音增长 Agent",
|
||||
"mission": "改成优先对标高互动账号。",
|
||||
"notes": "先压缩近期重点方向。",
|
||||
"status": "active",
|
||||
"config": {"focus": "engagement"},
|
||||
"reason": "调整当前抖音平台策略",
|
||||
},
|
||||
)
|
||||
self.assertEqual(update_profile.status_code, 200, update_profile.text)
|
||||
self.assertEqual(update_profile.json()["current_version"]["version_no"], 2)
|
||||
|
||||
rollback_profile = self.client.post(
|
||||
"/v2/platform-agents/douyin/profile/rollback",
|
||||
headers=self.ctx["member_headers"],
|
||||
json={
|
||||
"project_id": self.ctx["project_id"],
|
||||
"version_id": first_profile_version_id,
|
||||
"reason": "恢复到第一版平台 Agent 配置",
|
||||
},
|
||||
)
|
||||
self.assertEqual(rollback_profile.status_code, 200, rollback_profile.text)
|
||||
rollback_profile_payload = rollback_profile.json()
|
||||
self.assertEqual(rollback_profile_payload["current_version"]["rollback_from_version_id"], first_profile_version_id)
|
||||
self.assertEqual(rollback_profile_payload["config"]["focus"], "conversion")
|
||||
|
||||
profile_audits = self.client.get(
|
||||
"/v2/platform-agents/douyin/profile/audits",
|
||||
headers=self.ctx["member_headers"],
|
||||
params={"project_id": self.ctx["project_id"]},
|
||||
)
|
||||
self.assertEqual(profile_audits.status_code, 200, profile_audits.text)
|
||||
self.assertGreaterEqual(profile_audits.json()["count"], 3)
|
||||
|
||||
versions = self.client.get(
|
||||
f"/v2/platform-agents/douyin/skills/{skill_payload['id']}/versions",
|
||||
headers=self.ctx["member_headers"],
|
||||
@@ -857,6 +905,10 @@ class MainAgentGovernanceTests(unittest.TestCase):
|
||||
self.assertEqual(detail_response.status_code, 200, detail_response.text)
|
||||
detail_payload = detail_response.json()
|
||||
self.assertEqual(detail_payload["run_status"], "done")
|
||||
self.assertEqual(
|
||||
(((detail_payload.get("result") or {}).get("execution_card") or {}).get("platform_agent_profile") or {}).get("version_no"),
|
||||
rollback_profile_payload["current_version"]["version_no"],
|
||||
)
|
||||
|
||||
refreshed_agents = self.client.get(
|
||||
"/v2/platform-agents",
|
||||
@@ -866,9 +918,14 @@ class MainAgentGovernanceTests(unittest.TestCase):
|
||||
self.assertEqual(refreshed_agents.status_code, 200, refreshed_agents.text)
|
||||
refreshed_douyin = next(item for item in refreshed_agents.json()["items"] if item["platform"] == "douyin")
|
||||
self.assertIn("recent_execution", refreshed_douyin)
|
||||
self.assertEqual(refreshed_douyin["current_version"]["version_no"], rollback_profile_payload["current_version"]["version_no"])
|
||||
self.assertEqual(refreshed_douyin["recent_execution"]["run_id"], run_payload["id"])
|
||||
self.assertEqual(refreshed_douyin["recent_execution"]["intent_key"], "governance_review")
|
||||
self.assertGreaterEqual(refreshed_douyin["recent_execution"]["oneliner_profile_version_no"], 1)
|
||||
self.assertEqual(
|
||||
refreshed_douyin["recent_execution"]["platform_agent_profile_version_no"],
|
||||
rollback_profile_payload["current_version"]["version_no"],
|
||||
)
|
||||
|
||||
def test_admin_ops_routes_are_live(self) -> None:
|
||||
now = self.db_module.utc_now()
|
||||
|
||||
@@ -2019,6 +2019,7 @@ function renderOneLinerExecutionPayloadHtml(payload) {
|
||||
<span class="tag blue">${escapeHtml(platformAgentProfile.platform_label || platformLabel(platformAgentProfile.platform))}</span>
|
||||
${platformAgentProfile.name ? `<span class="tag">${escapeHtml(platformAgentProfile.name)}</span>` : ""}
|
||||
${platformAgentProfile.assistant_name ? `<span class="tag green">${escapeHtml(platformAgentProfile.assistant_name)}</span>` : ""}
|
||||
${platformAgentProfile.version_no ? `<span class="tag">${escapeHtml(platformLabel(platformAgentProfile.platform || payload.platform || ""))} Agent v${escapeHtml(formatNumber(platformAgentProfile.version_no || 0))}</span>` : ""}
|
||||
${platformAgentProfile.readiness_label ? `<span class="tag ${platformAgentProfile.readiness_score >= 75 ? "green" : platformAgentProfile.readiness_score >= 50 ? "blue" : "orange"}">${escapeHtml(platformAgentProfile.readiness_label)} ${escapeHtml(formatNumber(platformAgentProfile.readiness_score || 0))}</span>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
@@ -4458,6 +4459,7 @@ function renderPlatformAgentPanel() {
|
||||
<span class="tag blue">${escapeHtml(item.recent_execution.intent_label || "主 Agent 任务")}</span>
|
||||
<span class="tag">${escapeHtml(item.recent_execution.run_status || "done")}</span>
|
||||
${item.recent_execution.oneliner_profile_version_no ? `<span class="tag">配置 v${escapeHtml(formatNumber(item.recent_execution.oneliner_profile_version_no))}</span>` : ""}
|
||||
${item.recent_execution.platform_agent_profile_version_no ? `<span class="tag">${escapeHtml(item.platform_label || platformLabel(item.platform))} Agent v${escapeHtml(formatNumber(item.recent_execution.platform_agent_profile_version_no))}</span>` : ""}
|
||||
${item.recent_execution.source_screen ? `<span class="tag">${escapeHtml(screenLabel(item.recent_execution.source_screen) || item.recent_execution.source_screen)}</span>` : ""}
|
||||
</div>
|
||||
<div class="task-meta" style="margin-top:8px;">
|
||||
@@ -4469,6 +4471,7 @@ function renderPlatformAgentPanel() {
|
||||
<div class="task-meta" style="margin-top:10px;">
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-detail" data-platform="${escapeHtml(item.platform)}">查看详情</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-profile" data-platform="${escapeHtml(item.platform)}">配置</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-profile-history" data-platform="${escapeHtml(item.platform)}">看配置历史</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-memory" data-platform="${escapeHtml(item.platform)}">补记忆</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-skill" data-platform="${escapeHtml(item.platform)}">补技能</span>
|
||||
</div>
|
||||
@@ -9239,11 +9242,13 @@ function openPlatformAgentProfileAction(platform) {
|
||||
description: "给这个平台绑定自己的执行 Agent,并补充任务目标和方法论定位。",
|
||||
submitLabel: "保存平台 Agent",
|
||||
fields: [
|
||||
{ type: "html", label: "当前版本", html: renderPolicyVersionSummary(current, `当前 ${platformLabel(platform)} Agent 还没有历史版本。`) },
|
||||
{ name: "assistantId", label: "绑定执行 Agent", type: "select", value: current.assistant_id || assistants[0]?.value || "", options: [{ value: "", label: "先不绑定" }, ...assistants] },
|
||||
{ name: "name", label: "名称", value: current.name || `${platformLabel(platform)} Agent`, placeholder: "例如:快手增长 Agent" },
|
||||
{ name: "mission", label: "任务目标", type: "textarea", rows: 4, value: current.mission || "", placeholder: "例如:沉淀快手平台的开场结构、停留逻辑和转化方法论" },
|
||||
{ name: "notes", label: "补充说明", type: "textarea", rows: 4, value: current.notes || "", placeholder: "例如:优先观察短句节奏、直播切片和成交句式" },
|
||||
{ name: "status", label: "状态", type: "select", value: current.status || "active", options: [{ value: "active", label: "启用" }, { value: "draft", label: "草稿" }, { value: "paused", label: "暂停" }] }
|
||||
{ name: "status", label: "状态", type: "select", value: current.status || "active", options: [{ value: "active", label: "启用" }, { value: "draft", label: "草稿" }, { value: "paused", label: "暂停" }] },
|
||||
{ name: "reason", label: "变更原因", type: "textarea", rows: 3, value: "", placeholder: "例如:调整当前平台 Agent 的拆解重点和执行方向" }
|
||||
],
|
||||
onSubmit: async (values) => {
|
||||
const saved = await storyforgeFetch(`/v2/platform-agents/${encodeURIComponent(platform)}/profile`, {
|
||||
@@ -9255,6 +9260,7 @@ function openPlatformAgentProfileAction(platform) {
|
||||
mission: values.mission || "",
|
||||
notes: values.notes || "",
|
||||
status: values.status || "active",
|
||||
reason: values.reason || "",
|
||||
config: {
|
||||
self_optimize: true,
|
||||
tenant_scoped_memory: true,
|
||||
@@ -9263,7 +9269,44 @@ function openPlatformAgentProfileAction(platform) {
|
||||
}
|
||||
});
|
||||
appState.platformAgents = safeArray(appState.platformAgents).filter((item) => item.platform !== platform).concat(saved).sort((a, b) => String(a.platform).localeCompare(String(b.platform)));
|
||||
rememberAction("平台 Agent 已保存", `已更新 ${platformLabel(platform)} Agent。`, "green", saved);
|
||||
rememberAction("平台 Agent 已保存", `已更新 ${platformLabel(platform)} Agent,当前版本 ${saved.current_version?.version_no || 1}。`, "green", saved);
|
||||
renderAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function openPlatformAgentProfileHistoryAction(platform) {
|
||||
const project = requireSelectedProject();
|
||||
const normalizedPlatform = normalizePlatformValue(platform || getPreferredPlatform(), "douyin");
|
||||
const history = await loadPolicyVersions(`/v2/platform-agents/${encodeURIComponent(normalizedPlatform)}/profile/versions?project_id=${encodeURIComponent(project.id)}`);
|
||||
const audits = await storyforgeFetch(`/v2/platform-agents/${encodeURIComponent(normalizedPlatform)}/profile/audits?project_id=${encodeURIComponent(project.id)}`).catch(() => ({ items: [] }));
|
||||
const current = safeArray(appState.platformAgents).find((item) => item.platform === normalizedPlatform) || {};
|
||||
const selectedVersionId = history.items[0]?.id || "";
|
||||
openActionModal({
|
||||
title: `${platformLabel(normalizedPlatform)} Agent 配置历史`,
|
||||
description: "查看平台 Agent 配置版本,并从历史里选择一个版本回滚。",
|
||||
submitLabel: "回滚到所选版本",
|
||||
hideSubmit: !selectedVersionId,
|
||||
fields: [
|
||||
{ type: "html", label: "当前版本", html: renderPolicyVersionSummary(current, `当前 ${platformLabel(normalizedPlatform)} Agent 还没有历史版本。`) },
|
||||
{ type: "html", label: "历史版本", html: renderPolicyVersionsHtml(history.items, `当前 ${platformLabel(normalizedPlatform)} Agent 还没有历史版本。`) },
|
||||
{ type: "html", label: "审计记录", html: renderPolicyAuditsHtml(safeArray(audits.items || audits), "当前还没有审计记录。") },
|
||||
...(selectedVersionId ? [
|
||||
{ name: "versionId", label: "回滚版本", type: "select", value: selectedVersionId, options: safeArray(history.items).map((item) => ({ value: item.id, label: `v${formatNumber(item.version_no || 0)} · ${item.title || brief(item.summary || item.id, 24)}` })) },
|
||||
{ name: "reason", label: "回滚原因", type: "textarea", rows: 3, value: "", placeholder: "例如:恢复到上一版平台 Agent 方法论" }
|
||||
] : [])
|
||||
],
|
||||
onSubmit: async (values) => {
|
||||
const saved = await storyforgeFetch(`/v2/platform-agents/${encodeURIComponent(normalizedPlatform)}/profile/rollback`, {
|
||||
method: "POST",
|
||||
body: {
|
||||
project_id: project.id,
|
||||
version_id: values.versionId || selectedVersionId,
|
||||
reason: values.reason || ""
|
||||
}
|
||||
});
|
||||
appState.platformAgents = safeArray(appState.platformAgents).filter((item) => item.platform !== normalizedPlatform).concat(saved).sort((a, b) => String(a.platform).localeCompare(String(b.platform)));
|
||||
rememberAction(`${platformLabel(normalizedPlatform)} Agent 已回滚`, `已回滚到版本 ${saved.current_version?.version_no || "所选版本"}。`, "green", saved);
|
||||
renderAll();
|
||||
}
|
||||
});
|
||||
@@ -9394,6 +9437,7 @@ async function openPlatformAgentDetailAction(platform) {
|
||||
<span class="tag blue">${escapeHtml(profile.recent_execution.intent_label || "主 Agent 任务")}</span>
|
||||
<span class="tag">${escapeHtml(profile.recent_execution.run_status || "done")}</span>
|
||||
${profile.recent_execution.oneliner_profile_version_no ? `<span class="tag">配置 v${escapeHtml(formatNumber(profile.recent_execution.oneliner_profile_version_no))}</span>` : ""}
|
||||
${profile.recent_execution.platform_agent_profile_version_no ? `<span class="tag">${escapeHtml(platformLabel(normalizedPlatform))} Agent v${escapeHtml(formatNumber(profile.recent_execution.platform_agent_profile_version_no))}</span>` : ""}
|
||||
${profile.recent_execution.source_screen ? `<span class="tag">${escapeHtml(screenLabel(profile.recent_execution.source_screen) || profile.recent_execution.source_screen)}</span>` : ""}
|
||||
</div>
|
||||
<div class="task-meta" style="margin-top:8px;">
|
||||
@@ -9446,6 +9490,7 @@ async function openPlatformAgentDetailAction(platform) {
|
||||
<div class="task-meta" style="margin-top:12px;">
|
||||
<span class="tag clickable-tag" data-action="run-oneliner-action" data-executor-key="platform-self-check" data-platform="${escapeHtml(normalizedPlatform)}">运行平台自检</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-profile" data-platform="${escapeHtml(normalizedPlatform)}">编辑配置</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-profile-history" data-platform="${escapeHtml(normalizedPlatform)}">看配置历史</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-memory" data-platform="${escapeHtml(normalizedPlatform)}">继续补记忆</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-skill" data-platform="${escapeHtml(normalizedPlatform)}">继续补技能</span>
|
||||
<span class="tag clickable-tag" data-action="handoff-to-main-agent" data-platform="${escapeHtml(normalizedPlatform)}" data-source-screen="playbook" data-source-action-key="platform-agent-handoff" data-intent-key="custom" data-title="继续完善平台 Agent" data-goal="继续完善平台 Agent" data-summary="让主 Agent 结合当前平台记忆和技能,给出下一步执行计划。" data-plan-steps="${escapeHtml(JSON.stringify(["读取当前平台 Agent 配置", "检查记忆与技能缺口", "生成下一步执行计划"]))}">交给主 Agent 继续</span>
|
||||
@@ -11172,6 +11217,10 @@ document.addEventListener("click", async (event) => {
|
||||
openPlatformAgentProfileAction(action.dataset.platform || "");
|
||||
return;
|
||||
}
|
||||
if (name === "open-platform-agent-profile-history") {
|
||||
await openPlatformAgentProfileHistoryAction(action.dataset.platform || "");
|
||||
return;
|
||||
}
|
||||
if (name === "open-platform-agent-detail") {
|
||||
await openPlatformAgentDetailAction(action.dataset.platform || "");
|
||||
return;
|
||||
|
||||
@@ -749,6 +749,33 @@ test("main agent result rendering offers a direct route back into the recommende
|
||||
assert.match(lastAction, /recommended_action/);
|
||||
});
|
||||
|
||||
test("platform agent profiles expose history, rollback, and execution version context", () => {
|
||||
const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
|
||||
const profileEditor = extractBetween(APP, "function openPlatformAgentProfileAction(platform)", "async function openPlatformAgentProfileHistoryAction(platform)");
|
||||
const profileHistory = extractBetween(APP, "async function openPlatformAgentProfileHistoryAction(platform)", "function openPlatformAgentMemoryAction(platform)");
|
||||
const panel = extractBetween(APP, "function renderPlatformAgentPanel()", "function renderAdminOpsPanel()");
|
||||
const detail = extractBetween(APP, "async function openPlatformAgentDetailAction(platform)", "function openPlatformSkillReviewAction(platform, skillId, accepted)");
|
||||
const execution = extractBetween(APP, "function renderOneLinerExecutionPayloadHtml(payload)", "function parseOneLinerActionPayloadValue(value)");
|
||||
|
||||
assert.match(profileEditor, /renderPolicyVersionSummary\(current,/);
|
||||
assert.match(profileEditor, /name: "reason"/);
|
||||
assert.match(profileEditor, /saved\.current_version\?\.version_no/);
|
||||
|
||||
assert.match(profileHistory, /\/v2\/platform-agents\/\$\{encodeURIComponent\(normalizedPlatform\)\}\/profile\/versions/);
|
||||
assert.match(profileHistory, /\/v2\/platform-agents\/\$\{encodeURIComponent\(normalizedPlatform\)\}\/profile\/audits/);
|
||||
assert.match(profileHistory, /\/v2\/platform-agents\/\$\{encodeURIComponent\(normalizedPlatform\)\}\/profile\/rollback/);
|
||||
assert.match(profileHistory, /renderPolicyVersionsHtml/);
|
||||
assert.match(profileHistory, /renderPolicyAuditsHtml/);
|
||||
|
||||
assert.match(panel, /open-platform-agent-profile-history/);
|
||||
assert.match(detail, /open-platform-agent-profile-history/);
|
||||
assert.match(panel, /platform_agent_profile_version_no/);
|
||||
assert.match(detail, /platform_agent_profile_version_no/);
|
||||
assert.match(execution, /platformAgentProfile\.version_no/);
|
||||
assert.match(actions, /name === "open-platform-agent-profile-history"/);
|
||||
assert.match(actions, /openPlatformAgentProfileHistoryAction/);
|
||||
});
|
||||
|
||||
test("main agent route actions keep landing context and destination screens render a notice", () => {
|
||||
const execution = extractBetween(APP, "function renderOneLinerExecutionPayloadHtml(payload)", "function parseOneLinerActionPayloadValue(value)");
|
||||
const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
|
||||
|
||||
Reference in New Issue
Block a user