feat: prepare 2.2.0 with UI and execution fixes

Add dark theme, richer Markdown and readable plugin results.
Improve Android input evidence, response integrity and diagnostics.
Unify lifecycle, guidance receipts and evidence sources across clients.
Keep direct action dispatch without an added AI quota approval layer.
Align version metadata at 2.2.0 and bundle Helper 1.2.2.

Validation: 3063 backend tests before the version bump; 44 after.
Frontend: 317 tests and production build passed.
This commit is contained in:
chenmingxuan committed 2026-10-26 00:44:13 +08:00
1 parent 83bcfd54d5
commit fac4ccb760
146 files changed
+5689 -2033

No files matched your search

+32 -13
View File
@@ -35,6 +35,7 @@ from typing import Any
import uuid
from artemis.config.paths import get_temp_dir
from artemis.runtime.cancellation import CancellationReason, reason_from_code
CANCEL_DIR_NAME = "cancel-requests"
MARKER_TTL_SECONDS = 3600.0
@@ -92,6 +93,7 @@ def request_cancel(
pid: int | None = None,
process_created_at: float = 0.0,
reason: str = "",
cancel_reason: CancellationReason = CancellationReason.UNKNOWN,
) -> list[Path]:
"""Ask the worker identified by ``session_id`` and/or ``pid`` to cancel.
@@ -104,6 +106,7 @@ def request_cancel(
payload = {
"requested_at": now,
"reason": reason,
"cancel_reason": reason_from_code(cancel_reason).value,
"session_id": str(session_id) if session_id else None,
"pid": int(pid) if pid else None,
"process_created_at": float(process_created_at or 0.0),
@@ -129,7 +132,20 @@ def is_cancel_requested(
pid: int | None = None,
process_created_at: float | None = None,
) -> bool:
"""Return True when a live cancel marker targets this worker.
"""Return True when a live cancel marker targets this worker."""
return (
get_cancel_reason(session_id=session_id, pid=pid, process_created_at=process_created_at)
is not None
)
def get_cancel_reason(
*,
session_id: str | None = None,
pid: int | None = None,
process_created_at: float | None = None,
) -> CancellationReason | None:
"""Return the allowlisted cause of a live marker, or None without a request.
A PID marker is honoured only when its recorded process creation time is
absent or matches ``process_created_at`` (within one second), which
@@ -141,16 +157,16 @@ def is_cancel_requested(
if path.exists():
payload = _read_marker(path)
if not _is_expired(payload, path, now):
return True
return reason_from_code(payload.get("cancel_reason") if payload else None)
if pid:
path = _pid_marker(int(pid))
if path.exists():
payload = _read_marker(path)
if payload is None:
# Partially written or unreadable marker: retry on the next poll.
return False
return None
if _is_expired(payload, path, now):
return False
return None
try:
recorded = float(payload.get("process_created_at") or 0.0)
except (TypeError, ValueError):
@@ -161,9 +177,9 @@ def is_cancel_requested(
and process_created_at > 0
and abs(recorded - process_created_at) >= 1.0
):
return False
return True
return False
return None
return reason_from_code(payload.get("cancel_reason"))
return None
def clear_cancel_request(*, session_id: str | None = None, pid: int | None = None) -> None:
@@ -210,18 +226,21 @@ def current_process_created_at() -> float:
async def watch_for_cancel_request(
on_cancel: Callable[[], Any],
on_cancel: Callable[..., Any],
*,
session_id: str | None = None,
pid: int | None = None,
process_created_at: float | None = None,
poll_seconds: float | None = None,
pass_reason: bool = False,
) -> bool:
"""Poll for a cancel marker and invoke ``on_cancel`` once it appears.
Returns True when a cancellation was delivered. The coroutine runs until
it delivers one or is itself cancelled, so callers should run it as a
background task alongside the work it guards.
background task alongside the work it guards. With ``pass_reason``, the
callback receives an allowlisted CancellationReason; legacy callbacks
continue to take no arguments. Marker text is never forwarded.
"""
import asyncio
@@ -238,16 +257,16 @@ async def watch_for_cancel_request(
while True:
try:
requested = is_cancel_requested(
reason = get_cancel_reason(
session_id=session_id,
pid=pid,
process_created_at=process_created_at,
)
except Exception:
requested = False
if requested:
reason = None
if reason is not None:
clear_cancel_request(session_id=session_id, pid=pid)
result = on_cancel()
result = on_cancel(reason) if pass_reason else on_cancel()
if asyncio.iscoroutine(result):
await result
return True