feat: add official Codex skill and harden task execution

Install and update artemis-use with task diagnostics and plugin guidance. Preserve cross-device file receipts, review canonical reports, guard stage transitions and reduce small history summaries. Improve exploration lifecycle, request recovery and browser live connections.
This commit is contained in:
chenmingxuan committed 2026-10-18 14:18:37 +08:00
1 parent 2328a4df0c
commit e1ea099592
128 files changed
+4944 -700

No files matched your search

+50 -5
View File
@@ -254,18 +254,28 @@ def _mark_liveness_failure(trace_id: str, status_data: dict[str, Any]) -> None:
@mcp.tool()
def mobile_manage_task(
action: str,
trace_id: str,
trace_id: str = "",
instruction: str | None = None,
release_loop: bool = False,
request_id: str | None = None,
review_status: str | None = None,
review_reason: str = "",
user_confirmed: bool = False,
) -> dict[str, Any]:
"""Manages the lifecycle and retrieves the status of a background mobile automation task.
This is your primary diagnostic and control tool for tasks started by
`mobile_run_task` or the web workspace. You MUST use it to poll the task status when your
1-minute fallback timer triggers (as required by `mobile_run_task`). You
can also steer a subagent that is stuck or off-track, or abort a task.
`mobile_run_task` or the web workspace. Poll with reasonable intervals while
following a running task; use request diagnostics for unexplained waits.
You can also steer a runner that is stuck or off-track, or stop a task.
### Actions
- lookup: Resolve a submission by request_id without starting or resubmitting it.
- deletion_preview / delete: Show affected session IDs, then delete only when
the user requested it (user_confirmed=true). Active tasks cannot be deleted.
An exploration deletes its conversation runs, preserving independent reruns.
- review: Record ONLY the user's explicitly stated passed/failed decision with
review_reason and user_confirmed=true. Never label the agent's verdict as human.
- **'status'**: Returns `trace_id`, `status` ('running'/'pausing'/'task_paused'/
'resuming'/'completed'/'failed'/'cancelled'/'interrupted'), `device_serial` (which phone owns this task in multi-device
setups), `task_desc`, `model`, `elapsed_seconds`, a `test_summary` (when
@@ -311,6 +321,31 @@ def mobile_manage_task(
that authorizes the subagent to complete continuous monitoring
loops and finish the task gracefully.
"""
from mcp_server.tools.automation import _id, _request
import asyncio
if action == "lookup":
from mcp_server.task_requests import lookup
return lookup(_id(request_id))
_id(trace_id)
if action in {"deletion_preview", "delete", "review"}:
if action != "deletion_preview" and not user_confirmed:
raise ValueError("此动作需要用户明确要求;AI 自行判断不能记作人工复验")
if action == "deletion_preview":
return asyncio.run(_request("GET", f"/api/sessions/{trace_id}/deletion-preview"))
if action == "delete":
return asyncio.run(_request("POST", f"/api/sessions/{trace_id}/delete"))
if review_status not in {"passed", "failed"}:
raise ValueError("review_status 必须为 passed 或 failed")
result = asyncio.run(
_request(
"PUT",
f"/api/automation/reports/{trace_id}/review",
json={"status": review_status, "reason": review_reason},
)
)
return {"trace_id": trace_id, "test_result": result.get("test_result")}
status_data = trace_store.read_status(trace_id) or _web_session_status(trace_id)
if not status_data:
return {
@@ -438,6 +473,15 @@ def mobile_manage_task(
test_result = resolve_case_result(None, current_status, response.get("error") or "")
response["test_result"] = test_result.model_dump() if test_result else None
from apps.admin_console.services.case_review import reviewed_result
from apps.admin_console.services.automation_store import AutomationStore
from pathlib import Path
review_store = AutomationStore(Path(db_path))
response["test_result"] = reviewed_result(
trace_id, response.get("test_result"), review_store
)
if current_status in ("running", "pending", "task_paused", "resuming") and is_alive:
progress: dict[str, Any] = {}
@@ -472,12 +516,13 @@ def mobile_manage_task(
conn2.row_factory = sqlite3.Row
cur2 = conn2.cursor()
cur2.execute(
"SELECT count(*) as cnt FROM traces WHERE session_id = ? AND type = 'llm_call'",
"SELECT coalesce(max(step_number),0) as cnt FROM steps WHERE session_id = ?",
(session_id,),
)
row_cnt = cur2.fetchone()
if row_cnt:
progress["current_turn"] = row_cnt["cnt"]
progress["current_step"] = row_cnt["cnt"]
cur2.execute(
"SELECT payload FROM traces WHERE session_id = ? AND type in ('raw_thinking', 'llm_call') "