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

@@ -0,0 +1,147 @@
"""Bounded, read-only task diagnostics shared by MCP and HTTP clients."""
import os
import re
from apps.admin_console.database.repositories.session_repository import session_repo
from apps.admin_console.services import evidence_service, report_export
from apps.admin_console.services.automation_store import identifier, store
from apps.admin_console.services.request_diagnostics import request_diagnostics
_SECRET = re.compile(
r"(?i)^(api[_-]?key|access[_-]?token|refresh[_-]?token|authorization|password|secret|client_secret|token_key)$"
)
def public_value(value, *, max_chars: int = 60000):
"""Remove common credential forms and bound text without inventing missing evidence."""
remaining = max_chars
remaining_nodes = 2000
truncated = False
secrets = [
v
for k, v in os.environ.items()
if len(v) >= 8 and re.search(r"(?i)(api_?key|password|secret|token)$", k)
]
def clean(item, depth=0):
nonlocal remaining, remaining_nodes, truncated
remaining_nodes -= 1
if depth > 12:
truncated = True
return "[嵌套内容已截断]"
if isinstance(item, dict):
result = {}
for k, v in item.items():
if remaining_nodes <= 0 or len(result) >= 100:
truncated = True
break
key = clean(str(k), depth + 1)
result[key] = "[REDACTED]" if _SECRET.fullmatch(str(k)) else clean(v, depth + 1)
return result
if isinstance(item, list):
result = []
for v in item:
if remaining_nodes <= 0 or len(result) >= 100:
truncated = True
break
result.append(clean(v, depth + 1))
return result
if not isinstance(item, str):
return item
for secret in secrets:
item = item.replace(secret, "[REDACTED]")
item = re.sub(r"(?i)\bBearer\s+[^\s\"',;]+", "Bearer [REDACTED]", item)
item = re.sub(r"\bsk-[A-Za-z0-9_-]+", "[REDACTED]", item)
item = re.sub(r"(https?://)[^\s/@]+:[^\s/@]+@", r"\1[REDACTED]@", item)
item = re.sub(
r"""(?ix)((?:api[_-]?key|access[_-]?token|refresh[_-]?token|authorization|password|secret|token_key)["']?\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s&,;]+)""",
r"\1[REDACTED]",
item,
)
allowance = min(8000, remaining)
if len(item) > allowance:
truncated = True
item = item[:allowance] + "…[已截断;按步骤或关键词缩小范围]"
remaining = max(0, remaining - min(len(item), allowance))
return item
result = clean(value)
return {**result, "text_truncated": truncated, "content_truncated": truncated}
def page(items: list, offset: int, limit: int) -> dict:
return {
"items": items[offset : offset + limit],
"total": len(items),
"offset": offset,
"limit": limit,
"next_offset": offset + limit if offset + limit < len(items) else None,
}
def inspect_task(session_id: str, view: str, *, offset: int = 0, limit: int = 20) -> dict:
identifier(session_id)
if offset < 0 or not 1 <= limit <= 100:
raise ValueError("offset 必须非负,limit 必须在 1–100 之间")
if not session_repo.get_session_by_id(session_id) and not store.job(session_id):
raise LookupError("任务记录不存在")
if view == "usage":
result = session_repo.get_session_usage(session_id)
result["cache_hit_rate"] = (
result["cached_tokens"] / result["prompt_tokens"] if result["prompt_tokens"] else None
)
result["note"] = (
"缓存率=已记录 cached_tokens / prompt_tokens;无用量不等于零消耗,未上报的请求无法补推。"
)
elif view == "requests":
result = request_diagnostics(session_id, limit=limit, offset=offset)
elif view == "logs":
root = evidence_service.task_root(session_id)
result = {"logs": []}
for name in ("stdout.log", "stderr.log"):
path = evidence_service.evidence_file(root, name)
if path is None:
result["logs"].append({"name": name, "available": False})
continue
with path.open("rb") as stream:
size = path.stat().st_size
start = max(0, size - 128 * 1024)
stream.seek(start)
raw = stream.read(128 * 1024)
lines = raw.decode("utf-8", errors="replace").splitlines()
if start and lines:
lines = lines[1:]
# Offset counts backward from the newest line within the bounded tail.
end = max(0, len(lines) - offset)
result["logs"].append(
{
"name": name,
"available": True,
"bytes": size,
"lines": lines[max(0, end - limit) : end],
"offset": offset,
"next_offset": offset + limit if end > limit else None,
"older_bytes_omitted": start,
"order": "oldest_to_newest",
}
)
elif view in {"report", "evidence"}:
report = report_export.session_report(session_id)
if view == "report":
result = {**report, "steps": page(report.get("steps", []), offset, limit)}
else:
index = evidence_service.export_index(
session_id, report, report_export.session_videos(report)
)
result = {
**index,
**{
key: page(index.get(key, []), offset, limit)
for key in ("steps", "calls", "recordings", "android_videos", "files")
},
}
else:
raise ValueError("view 需为 report / usage / requests / logs / evidence")
return public_value({"session_id": session_id, **result})