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.
111 lines
4.6 KiB
Python
111 lines
4.6 KiB
Python
"""Guided exploration through the same HTTP service used by the workspace."""
|
|
|
|
from typing import Any, Literal
|
|
|
|
from mcp_server.base import mcp
|
|
from mcp_server.tools.automation import _id, _request
|
|
|
|
|
|
@mcp.tool()
|
|
async def mobile_exploration(
|
|
action: Literal[
|
|
"list",
|
|
"deletion_preview",
|
|
"delete",
|
|
"get",
|
|
"create",
|
|
"start",
|
|
"message",
|
|
"pause",
|
|
"continue",
|
|
"stop",
|
|
"demonstrate",
|
|
"demo_finish",
|
|
"demo_cancel",
|
|
"demo_extend",
|
|
"case",
|
|
"save_case",
|
|
"generate",
|
|
"validate",
|
|
"batch",
|
|
],
|
|
exploration_id: str | None = None,
|
|
request_id: str | None = None,
|
|
payload: dict[str, Any] | None = None,
|
|
demonstration_id: str | None = None,
|
|
after: int = 0,
|
|
version: int | None = None,
|
|
user_confirmed: bool = False,
|
|
) -> Any:
|
|
"""Explore ONE complex test with user guidance and produce a reusable case.
|
|
|
|
deletion_preview lists affected conversation runs; delete requires user_confirmed=true
|
|
after the user requests deletion. Independent validation runs are preserved.
|
|
Read-only list/get/case never execute tasks. create stores {goal,config?,task_config?};
|
|
start/continue explicitly enqueue or resume Pro exploration. Independent defaults:
|
|
gpt-6.1-sol/high, 1800 active seconds; timeout pauses, never fails by itself.
|
|
message payload={text}; answer pending questions or guide without discarding history.
|
|
demonstrate payload={source:'android'|'computer'|'both'} pauses at a safe point and
|
|
records while retaining the device/plugin lease. Wait for recording before the user
|
|
operates. demo_finish analyzes the SAVED clip; demo_extend payload={seconds:900};
|
|
demo_cancel preserves evidence. Default 900s, warning 60s before expiry, auto-pause.
|
|
save_case payload={expected_version,case:{name,goal,preconditions?,evidence?}};
|
|
generate payload={source_run_id,expected_version} starts evidence-based synthesis,
|
|
poll get.generations for completion. validate payload={version,profile,task_config?}
|
|
explicitly runs the saved version in Flash/Pro; batch additionally accepts name and
|
|
creates a DRAFT batch, never starts it. Exploration pass and rerun pass are separate.
|
|
Use stable request_id for every mutation except pause/stop/delete, reusing it on transport
|
|
retries. No API keys; configure secrets in the Web UI. mobile_config(section='exploration')
|
|
controls non-secret defaults. After errors call mobile_diagnose before resubmitting.
|
|
"""
|
|
base = "/api/explorations"
|
|
if action in {"deletion_preview", "delete"}:
|
|
path = f"{base}/{_id(exploration_id)}"
|
|
if action == "deletion_preview":
|
|
return await _request("GET", path + "/deletion-preview")
|
|
if not user_confirmed:
|
|
raise ValueError("删除探索需要用户明确要求;先用 deletion_preview 核对影响范围")
|
|
return await _request("DELETE", path)
|
|
|
|
if action in {"list", "get", "case"}:
|
|
if payload or request_id:
|
|
raise ValueError("查询不接受写入内容")
|
|
if action == "list":
|
|
return await _request("GET", base)
|
|
path = f"{base}/{_id(exploration_id)}"
|
|
if action == "get":
|
|
if after < 0:
|
|
raise ValueError("after 不能小于零")
|
|
return await _request("GET", path, params={"after": after})
|
|
return await _request(
|
|
"GET", path + "/case", params={"version": version} if version is not None else {}
|
|
)
|
|
if action not in {"pause", "stop"} and (not request_id or not request_id.strip()):
|
|
raise ValueError("写入探索需提供稳定的 request_id")
|
|
body = dict(payload or {})
|
|
if "request_id" in body:
|
|
raise ValueError("request_id 请使用单独参数")
|
|
if request_id:
|
|
body["request_id"] = request_id
|
|
if action == "create":
|
|
return await _request("POST", base, json=body, params={"source": "mcp"})
|
|
suffix = {
|
|
"start": "start",
|
|
"message": "messages",
|
|
"pause": "pause",
|
|
"continue": "continue",
|
|
"stop": "stop",
|
|
"demonstrate": "demonstrations",
|
|
"save_case": "case",
|
|
"generate": "generate",
|
|
"validate": "validate",
|
|
"batch": "batch",
|
|
}.get(action)
|
|
if action.startswith("demo_"):
|
|
suffix = f"demonstrations/{_id(demonstration_id)}/{action.removeprefix('demo_')}"
|
|
if suffix is None:
|
|
raise ValueError("不支持的探索操作")
|
|
return await _request(
|
|
"POST", f"{base}/{_id(exploration_id)}/{suffix}", json=body, params={"source": "mcp"}
|
|
)
|