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,109 @@
|
||||
"""Small execution-stage ledger; no extra planner/model or scheduler."""
|
||||
|
||||
import json
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
from artemis.core.tool_declaration import ToolDeclaration
|
||||
from artemis.utils.notes import save_verified_note
|
||||
|
||||
|
||||
class Stage(BaseModel):
|
||||
id: str = Field(min_length=1, max_length=80)
|
||||
scope: str = Field(
|
||||
min_length=1,
|
||||
max_length=600,
|
||||
description="Requirements covered by this stage, including its exit condition.",
|
||||
)
|
||||
resources: list[str] = Field(
|
||||
min_length=1,
|
||||
max_length=8,
|
||||
description="android or actual plugin IDs; several only when the stage requires interleaving.",
|
||||
)
|
||||
status: Literal["pending", "active", "done", "blocked"]
|
||||
evidence: str = Field(
|
||||
default="",
|
||||
max_length=1200,
|
||||
description="Observed facts and step/file references; required for done/blocked. done means scope checked, not test passed.",
|
||||
)
|
||||
|
||||
|
||||
class ProgressUpdate(BaseModel):
|
||||
stages: list[Stage] = Field(min_length=1, max_length=16)
|
||||
reason: str = Field(
|
||||
min_length=1,
|
||||
max_length=600,
|
||||
description="Why the stage changed; when replanning, explain changed requirements or blocking evidence.",
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def ordered(self):
|
||||
if len({s.id for s in self.stages}) != len(self.stages):
|
||||
raise ValueError("阶段 id 不能重复")
|
||||
if sum(s.status == "active" for s in self.stages) > 1:
|
||||
raise ValueError("只能有一个当前阶段")
|
||||
outstanding = False
|
||||
for stage in self.stages:
|
||||
if stage.status in {"done", "blocked"} and not stage.evidence.strip():
|
||||
raise ValueError("完成或阻塞阶段必须记录事实与步骤/文件依据")
|
||||
if stage.status in {"active", "done"} and outstanding:
|
||||
raise ValueError("前面的阶段尚未检查完,不能提前推进;需先完成或注明阻塞")
|
||||
outstanding = outstanding or stage.status in {"active", "pending"}
|
||||
return self
|
||||
|
||||
|
||||
PROGRESS_TOOL = ToolDeclaration(
|
||||
"update_task_progress",
|
||||
"Record ordered stages for a cross-resource task before acting. Follow the user's sequence; "
|
||||
"read-only preflight is allowed on other resources. Update only at stage boundaries or genuine "
|
||||
"replanning, not every turn. Can precede the next action in the same tool-call list. "
|
||||
"Keep outstanding scope; done requires recorded evidence. This ledger does not verify business success.",
|
||||
ProgressUpdate.model_json_schema(),
|
||||
)
|
||||
|
||||
|
||||
class TaskProgress:
|
||||
def __init__(self):
|
||||
self.value: ProgressUpdate | None = None
|
||||
self.last_resource: str | None = None
|
||||
|
||||
def update(self, args: dict, *, resources: set[str], directory=None) -> str:
|
||||
update = ProgressUpdate.model_validate(args)
|
||||
if any(set(stage.resources) - resources for stage in update.stages):
|
||||
raise ValueError(f"阶段引用了不可用资源;可用:{sorted(resources)}")
|
||||
if self.value:
|
||||
missing = {s.id for s in self.value.stages} - {s.id for s in update.stages}
|
||||
if missing:
|
||||
raise ValueError(
|
||||
f"不能删除既有阶段 {sorted(missing)};保留范围并记录完成依据或阻塞原因"
|
||||
)
|
||||
if directory:
|
||||
save_verified_note(directory, "task_progress", update.model_dump_json(indent=2))
|
||||
self.value = update
|
||||
return json.dumps(
|
||||
{
|
||||
"ok": True,
|
||||
"active": next((s.id for s in update.stages if s.status == "active"), None),
|
||||
"saved": bool(directory),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
def guard(self, resource: str | None, *, completed: bool = False) -> str | None:
|
||||
if completed and self.value:
|
||||
missing = [s.scope for s in self.value.stages if s.status in {"active", "pending"}]
|
||||
if missing:
|
||||
return (
|
||||
"仍有未检查完的阶段:" + "; ".join(missing) + "。先核对并更新进度,再结束任务。"
|
||||
)
|
||||
if resource is None:
|
||||
return None
|
||||
if self.value:
|
||||
active = next((s for s in self.value.stages if s.status == "active"), None)
|
||||
if active is None or resource not in active.resources:
|
||||
scope = active.scope if active else "尚未选择"
|
||||
return f"当前阶段:{scope};暂未执行 {resource} 操作。先完成当前阶段或根据真实阻塞调整 update_task_progress;其他端只读核验仍可进行。"
|
||||
elif self.last_resource and resource != self.last_resource:
|
||||
return "跨端操作前需用 update_task_progress 记录已完成与待做范围,遵循用户指定顺序;只读预检不能直接扩展为另一端整段操作。"
|
||||
return None
|
||||
Reference in new issue
Block a user