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:
1 parent
2328a4df0c
commit
e1ea099592
128 files changed
+4944
-700
No files matched your search
@@ -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
|
||||
@@ -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 "工具已返回;请根据证据判断业务结果。"
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user