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

+62
View File
@@ -0,0 +1,62 @@
"""Model-facing runtime receipts, outside the truncatable plugin observation."""
import json
from artemis.plugins.prompt_budget import bounded_result_text
def attachment_fields(attachments: list) -> list[dict]:
return [
{
key: value[:1000] if isinstance(value, str) else value
for key in ("path", "name", "bytes", "sha256", "media_type")
if isinstance(value := item.get(key), (str, int))
}
for item in attachments[:8]
if isinstance(item, dict)
]
def step_attachment_receipts(step: dict) -> str:
"""Keep actual exported paths through every history compaction level."""
result = step.get("last_execution_result")
actions = result.get("execution") if isinstance(result, dict) else None
if not isinstance(actions, list):
actions = step.get("action_taken")
actions = actions if isinstance(actions, list) else [actions]
rows = []
for action in actions:
if not isinstance(action, dict) or not action.get("plugin_id"):
continue
attachments = action.get("attachments")
if isinstance(attachments, list) and attachments:
rows.append(
{
"plugin": action["plugin_id"],
"attachments": attachment_fields(attachments),
**({"omitted": len(attachments) - 8} if len(attachments) > 8 else {}),
}
)
return json.dumps(rows, ensure_ascii=False) if rows else ""
def result_blocks(text: str, *, call_id: str = "", attachments=None) -> list[dict]:
blocks = []
if isinstance(attachments, list) and attachments:
# Only callers holding runtime-validated metadata may supply these.
# Never extract alleged receipts from plugin prose/structured_content.
receipts = attachment_fields(attachments)
receipt = {"attachments": receipts}
if len(attachments) > 8:
receipt["omitted"] = len(attachments) - 8
blocks.append(
{
"type": "text",
"text": (
"ARTEMIS verified file receipts (task trace paths; content correctness is not verified):\n"
+ json.dumps(receipt, ensure_ascii=False)
),
}
)
blocks.append({"type": "text", "text": bounded_result_text(text, call_id=call_id)})
return blocks
+15 -2
View File
@@ -229,9 +229,22 @@ class PluginCallResult(PluginModel):
def text(self) -> str:
import json
parts = [str(block["text"]) for block in self.content if block.get("type") == "text"]
parts = list(
dict.fromkeys(
str(block["text"]) for block in self.content if block.get("type") == "text"
)
)
if self.structured_content is not None:
parts.append(json.dumps(self.structured_content, ensure_ascii=False))
# MCP commonly returns the same accessibility tree in both channels.
# Keep one exact copy, without changing the persisted raw response.
visible_text = "\n".join(parts)
structured = {
key: "[identical text already included above]"
if isinstance(value, str) and len(value) >= 256 and value in visible_text
else value
for key, value in self.structured_content.items()
}
parts.append(json.dumps(structured, ensure_ascii=False))
if self.message:
parts.append(self.message)
return "\n".join(parts) or "工具已返回;请根据证据判断业务结果。"
+6 -3
View File
@@ -9,7 +9,7 @@ from langchain_core.messages import BaseMessage
from PIL import Image
from artemis.tools.tool_wrapper import tool_result_messages
from artemis.plugins.prompt_budget import bounded_result_text
from artemis.plugins.delivery import result_blocks
def plugin_result_messages(report: Any, engine: Any) -> dict[str, list[BaseMessage]]:
@@ -24,8 +24,11 @@ def plugin_result_messages(report: Any, engine: Any) -> dict[str, list[BaseMessa
continue
status = action.get("plugin_status")
text = action.get("result") or "此插件操作未完成或未执行;请重新观察后再决定下一步。"
text = bounded_result_text(str(text), call_id=str(action.get("text_ref") or ""))
blocks = [{"type": "text", "text": text}]
blocks = result_blocks(
str(text),
call_id=str(action.get("text_ref") or ""),
attachments=action.get("attachments"),
)
names = action.get("image_names") or [action.get("post_image_name")]
for name in dict.fromkeys(names):
if not name or engine is None:
+6 -4
View File
@@ -567,14 +567,16 @@ class PluginRuntime:
@staticmethod
def content_blocks(outcome: PluginCallResult) -> list[dict]:
from artemis.plugins.prompt_budget import bounded_result_text
from artemis.plugins.delivery import result_blocks
text = outcome.text()
# Explicit paged reads already have a fixed page size and must not
# be clipped a second time (which would leave gaps between pages).
if outcome.plugin_id:
text = bounded_result_text(text, call_id=outcome.text_ref)
blocks: list[dict] = [{"type": "text", "text": text}]
blocks: list[dict] = (
result_blocks(text, call_id=outcome.text_ref, attachments=outcome.attachments)
if outcome.plugin_id
else [{"type": "text", "text": text}]
)
for block in outcome.content:
if block.get("type") == "image" and block.get("mimeType") in {
"image/png",