feat: harden agent verification and runtime contracts

Introduce two-gate checker settlement, explicit graph visibility and checkpoints, structured LLM reliability, centralized action specifications, and corresponding tests and design documentation.
This commit is contained in:
somew1nd committed 2026-08-31 20:42:24 -07:00
1 parent ddfa420945
commit cbe8f4f0c6
106 files changed
+11542 -4445

No files matched your search

+1 -1
View File
@@ -69,7 +69,7 @@ When you run `uv run artemis mcp --install all` (or target a specific IDE like `
## 🛠️ MCP Tools Overview
* **`mobile_run_task`**: Asynchronously launches an autonomous mobile automation task (`Flash` or `Pro` model) with optional `device_serial` targeting.
* **`mobile_manage_task`**: Manages task lifecycle (`status`, `stop`, `inject_instruction`), returning task state and assigned `device_serial`.
* **`mobile_manage_task`**: Manages task lifecycle (`status`, `stop`, `inject_instruction`), returning task state and assigned `device_serial`. Pass `release_loop=True` with `inject_instruction` to gracefully end a `[Loop:continuous]` monitoring task — this explicit signal (not "please stop" wording) is what unlocks the loop milestone's completion.
* **`mobile_get_device_state`**: Real-time observer (`screenshot` or OCR+XML `hierarchy`) with optional `device_serial`.
* **`mobile_inspect_trace`**: Granular trace inspection, visual action overlays, agent reasoning, and `device_serial` tracking.
+48 -7
View File
@@ -192,7 +192,10 @@ def _mark_liveness_failure(trace_id: str, status_data: dict[str, Any]) -> None:
@mcp.tool()
def mobile_manage_task(
action: str, trace_id: str, instruction: str | None = None
action: str,
trace_id: str,
instruction: str | None = None,
release_loop: bool = False,
) -> dict[str, Any]:
"""Manages the lifecycle and retrieves the status of a background mobile automation task.
@@ -204,23 +207,35 @@ def mobile_manage_task(
### Actions
- **'status'**: Returns `trace_id`, `status` ('running'/'completed'/'failed'/
'cancelled'), `device_serial` (which phone owns this task in multi-device
setups), `task_desc`, `model`, `elapsed_seconds`, and `progress` (Flash:
setups), `task_desc`, `model`, `elapsed_seconds`, a `test_summary` (when
the run declared verification check items: machine-readable
passed/failed/inconclusive/unchecked counts plus failed-item details —
no need to parse report prose), and `progress` (Flash:
current turn, latest thought/action — intervene if turns climb without
progress or the thought indicates it is stuck; Pro: the active task plan
— check it still aligns with your goal).
- **'inject_instruction'**: Injects real-time guidance mid-flight when the
subagent errs, stalls, or loops. Applied at the start of the next
planning turn (Pro) or the next reactive loop (Flash). Requires
`instruction`.
`instruction` (unless `release_loop=True`). To end a continuous
monitoring task ([Loop:continuous] milestone) gracefully, you MUST pass
`release_loop=True` — this is the only signal that unlocks the
milestone's completion; natural-language "please stop" phrasing in
`instruction` is NOT interpreted as a stop signal.
- **'stop'**: Forcefully terminates the subagent, immediately halting
device interactions and releasing the device. Use when the task is done,
irreparably broken, or running out of control.
irreparably broken, or running out of control. Prefer
`inject_instruction` with `release_loop=True` when a monitoring task
should wind down cleanly instead of being killed.
Args:
action: `"status"`, `"inject_instruction"`, or `"stop"`.
trace_id: The task's session identifier from `mobile_run_task`.
instruction: Guidance string; required for `inject_instruction`, omit
otherwise.
instruction: Guidance string; required for `inject_instruction`
(optional when `release_loop=True`), omit otherwise.
release_loop: With `inject_instruction`: explicit user stop signal
that authorizes the subagent to complete continuous monitoring
loops and finish the task gracefully.
"""
status_data = trace_store.read_status(trace_id)
if not status_data:
@@ -298,6 +313,25 @@ def mobile_manage_task(
elif current_status == "completed":
response["result"] = status_data.get("result")
# Machine-readable test summary (written by exit settlement): callers
# get assertion results without parsing report prose.
for candidate in (
os.path.join(trace_store.TRACES_DIR, trace_id, "run_outcome.json"),
os.path.join(trace_dir, "run_outcome.json"),
):
if os.path.exists(candidate):
try:
with open(candidate, encoding="utf-8") as f:
run_outcome = json.load(f)
tests = run_outcome.get("tests") or {}
response["test_summary"] = {
"task_status": run_outcome.get("task_status"),
**tests,
}
except Exception:
pass
break
if current_status in ("running", "pending") and is_alive:
progress: dict[str, Any] = {}
@@ -491,6 +525,11 @@ def mobile_manage_task(
}
elif action == "inject_instruction":
if not instruction and release_loop:
instruction = (
"The user has requested to stop the continuous monitoring loop."
" Wrap up gracefully and complete the task."
)
if not instruction:
return {
"trace_id": trace_id,
@@ -518,16 +557,18 @@ def mobile_manage_task(
json.dump(
{
"instruction": instruction,
"release_loop": bool(release_loop),
"timestamp": time.time(),
"status": "pending",
},
f,
indent=2,
)
suffix = " (with release_loop stop signal)" if release_loop else ""
return {
"trace_id": trace_id,
"status": current_status,
"message": f"Successfully injected instruction: '{instruction}'",
"message": f"Successfully injected instruction{suffix}: '{instruction}'",
}
except Exception as e:
return {