From f320ec1b44e6ddb07ee85f783c0d87b625f56d6d Mon Sep 17 00:00:00 2001 From: kris Date: Sun, 5 Apr 2026 06:30:53 +0800 Subject: [PATCH] feat: add direct tracking digest actions --- CHANGELOG.md | 6 +++ collector-service/app/oneliner_features.py | 50 ++++++++++++++++++++++ tests/test_main_agent_governance.py | 18 ++++++++ 3 files changed, 74 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 357a625..84b6d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,12 @@ - 如果本轮只生成了一条同步任务,结果会直接落到任务详情;如果是多条批量同步,则回到跟踪工作区继续看结果。 - 治理回归补上了这条动作的断言,锁住动作注册表、批量同步执行和推荐落点都必须保持 live。 +### 主 Agent 可直接标记跟踪日报已读 + +- `OneLiner / 主 Agent` 现在新增了 `直接标记日报已读` 动作,会直接调用 `/v2/{platform}/tracking/cursor` 更新当前平台的已读游标。 +- 这让跟踪流不再只停在“同步一批账号”,而是可以顺手把这轮日报窗口标成已处理。 +- 动作完成后会统一回到 `跟踪工作区`,继续看当前平台的日报和下一步跟进动作。 + ### 额度编辑弹层补成真正的套餐配置器 - `编辑租户额度` 不再只是裸数字表单,而是会即时预览当前套餐的预算、动作池和预警阈值。 diff --git a/collector-service/app/oneliner_features.py b/collector-service/app/oneliner_features.py index 8b5bf91..0969e42 100644 --- a/collector-service/app/oneliner_features.py +++ b/collector-service/app/oneliner_features.py @@ -346,6 +346,16 @@ ACTION_REGISTRY_DEFAULTS: dict[str, dict[str, Any]] = { "requires_platform": True, "config": {}, }, + "mark-tracking-read": { + "label": "直接标记日报已读", + "description": "把当前平台跟踪日报更新为已读,下次从新的时间点继续汇总。", + "category": "tracking", + "handler_key": "mark-tracking-read", + "status": "enabled", + "admin_only": False, + "requires_platform": True, + "config": {}, + }, "create-assistant": { "label": "直接创建 Agent", "description": "根据当前项目和平台上下文,直接创建可继续编辑的 Agent。", @@ -4563,6 +4573,15 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None: "platform": plan.get("platform", ""), } ) + secondary_actions.append( + { + "key": "run-oneliner-action", + "label": "直接标记日报已读", + "kind": "api_action", + "executor_key": "mark-tracking-read", + "platform": plan.get("platform", ""), + } + ) if context_assistant: secondary_actions.append( { @@ -5869,6 +5888,36 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None: "recommended_action": recommended_action, } + async def _run_mark_tracking_read() -> dict[str, Any]: + if not normalized_platform: + raise HTTPException(status_code=400, detail="Platform is required for tracking cursor update") + last_seen_at = str( + requested_payload.get("last_seen_at") + or requested_payload.get("lastSeenAt") + or legacy.utc_now() + ).strip() or legacy.utc_now() + payload = await _call_local_api( + account, + method="POST", + path=f"/v2/{normalized_platform}/tracking/cursor", + json_body={"last_seen_at": last_seen_at}, + ) + return { + "title": "OneLiner 已标记日报已读", + "summary": f"已把 {legacy.platform_label(normalized_platform)} 跟踪日报更新为已读,后续将从新的时间点继续汇总。", + "payload": { + "platform": normalized_platform, + "cursor": payload, + }, + "recommended_action": _recommended_action( + "goto-tracking", + label="回到跟踪工作区", + summary="继续查看当前平台跟踪日报和下一步跟进动作。", + screen="tracking", + platform=normalized_platform, + ), + } + async def _run_analyze_top_videos() -> dict[str, Any]: if not normalized_platform: raise HTTPException(status_code=400, detail="Platform is required for top video analysis") @@ -6174,6 +6223,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None: "analyze-account": _run_analyze_account, "track-account": _run_track_account, "refresh-tracking": _run_refresh_tracking, + "mark-tracking-read": _run_mark_tracking_read, "analyze-top-videos": _run_analyze_top_videos, "create-assistant": _run_create_assistant, "create-ai-video": _run_create_ai_video, diff --git a/tests/test_main_agent_governance.py b/tests/test_main_agent_governance.py index e5a4e8d..cb3baeb 100644 --- a/tests/test_main_agent_governance.py +++ b/tests/test_main_agent_governance.py @@ -800,6 +800,7 @@ class MainAgentGovernanceTests(unittest.TestCase): self.assertIn("analyze-account", action_keys) self.assertIn("track-account", action_keys) self.assertIn("refresh-tracking", action_keys) + self.assertIn("mark-tracking-read", action_keys) self.assertIn("create-assistant", action_keys) save_registry = self.client.put( @@ -1122,6 +1123,23 @@ class MainAgentGovernanceTests(unittest.TestCase): self.assertEqual(refresh_tracking_payload["payload"]["platform"], "kuaishou") self.assertGreaterEqual(int(refresh_tracking_payload["payload"]["refresh"]["refreshed"] or 0), 1) + mark_read_response = self.client.post( + "/v2/oneliner/actions/execute", + headers=self.ctx["member_headers"], + json={ + "action_key": "mark-tracking-read", + "project_id": self.ctx["project_id"], + "platform": "kuaishou", + "payload": {"last_seen_at": "2026-04-05T00:00:00+00:00"}, + }, + ) + self.assertEqual(mark_read_response.status_code, 200, mark_read_response.text) + mark_read_payload = mark_read_response.json() + self.assertEqual(mark_read_payload["recommended_action"]["action"], "goto-tracking") + self.assertEqual(mark_read_payload["recommended_action"]["screen"], "tracking") + self.assertEqual(mark_read_payload["payload"]["platform"], "kuaishou") + self.assertEqual(mark_read_payload["payload"]["cursor"]["last_seen_at"], "2026-04-05T00:00:00+00:00") + create_response = self.client.post( "/v2/oneliner/actions/execute", headers=self.ctx["member_headers"],