diff --git a/.agents/skills/upload-delivery-desk-work/SKILL.md b/.agents/skills/upload-delivery-desk-work/SKILL.md index 18559c6..0ad7005 100644 --- a/.agents/skills/upload-delivery-desk-work/SKILL.md +++ b/.agents/skills/upload-delivery-desk-work/SKILL.md @@ -12,6 +12,7 @@ Use a two-phase plan/apply workflow. Optimize for correct placement, not speed. - Treat the product hierarchy as `operation group -> project -> work -> review round`. - Never use a collection/delivery-set identifier. `collections`, `notes`, and `versions` are legacy compatibility names. - Never infer a project or work from a partial name, page position, recent activity, or a remembered ID. +- At the start of every invocation, discover and explicitly ask the operator to confirm the exact operation group and project. A target confirmation from an earlier invocation cannot be reused. - Never create a work until the operator confirms the resolved group, project, content, image order, and confirmation code. - Never create a new round until the operator confirms the resolved work and current round. - Never retry a timed-out create request. First inspect current server state; otherwise a retry can create a duplicate round. @@ -38,7 +39,7 @@ Do not create groups, projects, API keys, feedback, or review decisions with thi For both operations, require: -- Delivery Desk base URL. Default to `DELIVERY_DESK_BASE_URL` or `http://127.0.0.1:3010` only for local development. +- Use the fixed Delivery Desk API address `http://192.168.30.90:3010`. Do not ask the operator to configure it. Use `--base-url` only when the operator explicitly instructs you to migrate or test another environment. - A valid API key in `DELIVERY_DESK_API_KEY`. - Exact target project, resolved to group ID/name and project ID/name/slug. - Ordered public image URLs or ordered local image paths. @@ -57,7 +58,7 @@ For `create_round`, also require: When information is missing, ask one concise question listing only the missing fields. Do not proceed to mutation. -## 3. Discover authoritative IDs +## 3. Discover and confirm the authoritative target Use the bundled script from the skill directory: @@ -73,8 +74,9 @@ Resolution rules: 1. Match IDs first. 2. Validate the project ID against its returned group ID, group name, project name, and slug. 3. If the operator supplied only names, list exact matches with IDs and ask the operator to choose when zero or multiple matches exist. -4. Even with one match, show the resolved identity before creating the plan. -5. For a new round, verify the work belongs to the confirmed project. +4. Even when the request already names a target or only one project is accessible, show the resolved group name/ID and project name/ID/slug and ask: `本次操作目标是否为:运营组「」(ID ) / 项目「」(ID , slug )?请回复“确认目标”。` +5. Accept only an explicit target confirmation given during the current invocation. Do not generate `plan-work` or `plan-round` before it. +6. For a new round, verify the work belongs to the confirmed project. Do not silently choose the only project merely because an API key currently exposes one. diff --git a/.agents/skills/upload-delivery-desk-work/agents/openai.yaml b/.agents/skills/upload-delivery-desk-work/agents/openai.yaml index 5ba1ce0..7b6349d 100644 --- a/.agents/skills/upload-delivery-desk-work/agents/openai.yaml +++ b/.agents/skills/upload-delivery-desk-work/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "上传 Delivery Desk 作品" short_description: "精确定位运营组与项目,安全创建作品或提交新的验收轮次" - default_prompt: "Use $upload-delivery-desk-work to safely locate the exact project and upload a work or a new review round." + default_prompt: "Use $upload-delivery-desk-work to discover and ask me to confirm the exact operation group and project before uploading a work or a new review round." diff --git a/.agents/skills/upload-delivery-desk-work/scripts/delivery_desk_upload.py b/.agents/skills/upload-delivery-desk-work/scripts/delivery_desk_upload.py index 4eb4991..dadff2d 100644 --- a/.agents/skills/upload-delivery-desk-work/scripts/delivery_desk_upload.py +++ b/.agents/skills/upload-delivery-desk-work/scripts/delivery_desk_upload.py @@ -19,6 +19,7 @@ from urllib.parse import quote, urlsplit from urllib.request import Request, urlopen MAX_LOCAL_IMAGE_BYTES = 20 * 1024 * 1024 +DEFAULT_BASE_URL = "http://192.168.30.90:3010" LOCAL_IMAGE_TYPES = { ".jpg": "image/jpeg", ".jpeg": "image/jpeg", @@ -75,7 +76,7 @@ def api_key() -> str: def base_url(value: str | None = None) -> str: - return (value or os.getenv("DELIVERY_DESK_BASE_URL") or "http://127.0.0.1:3010").rstrip("/") + return (value or DEFAULT_BASE_URL).rstrip("/") def request_json(base: str, path: str, *, method: str = "GET", body: dict[str, Any] | None = None) -> tuple[int, Any]: @@ -461,7 +462,7 @@ def cmd_apply(args: argparse.Namespace) -> None: def add_common(command: argparse.ArgumentParser) -> None: - command.add_argument("--base-url", default=None) + command.add_argument("--base-url", default=None, help=f"Delivery Desk API 地址(默认:{DEFAULT_BASE_URL})") def add_content(command: argparse.ArgumentParser, *, title_required: bool) -> None: diff --git a/docs/integration-guide.md b/docs/integration-guide.md index d3b99e2..d43d4b7 100644 --- a/docs/integration-guide.md +++ b/docs/integration-guide.md @@ -12,7 +12,7 @@ Authorization: Bearer dd_live_xxx ## Agent 安全上传 Skill -项目内置 `.agents/skills/upload-delivery-desk-work`,用于引导 Agent 精确定位运营组、项目和作品后创建作品或提交新验收轮次。它强制执行“发现 → 生成计划 → 操作者确认 → 单次提交 → 回读验证”,不允许根据名称猜测目标。 +项目内置 `.agents/skills/upload-delivery-desk-work`,用于引导 Agent 精确定位运营组、项目和作品后创建作品或提交新验收轮次。它固定连接 `http://192.168.30.90:3010`,并强制执行“发现 → 操作者确认运营组与项目 → 生成计划 → 操作者确认内容 → 单次提交 → 回读验证”,不允许根据名称猜测目标。 更新 Skill 后重新生成分发包: diff --git a/skill-packages/upload-delivery-desk-work.zip b/skill-packages/upload-delivery-desk-work.zip index a2ff832..63e9c38 100644 Binary files a/skill-packages/upload-delivery-desk-work.zip and b/skill-packages/upload-delivery-desk-work.zip differ diff --git a/tests/test_upload_skill.py b/tests/test_upload_skill.py index 8093b1f..70f6384 100644 --- a/tests/test_upload_skill.py +++ b/tests/test_upload_skill.py @@ -70,6 +70,21 @@ def apply_silently(path: Path, code: str) -> dict: def main() -> None: + previous_base_url = os.environ.get("DELIVERY_DESK_BASE_URL") + os.environ["DELIVERY_DESK_BASE_URL"] = "http://should-not-override.invalid" + try: + assert MODULE.base_url() == "http://192.168.30.90:3010" + assert MODULE.base_url("https://delivery.example.com/") == "https://delivery.example.com" + finally: + if previous_base_url is None: + os.environ.pop("DELIVERY_DESK_BASE_URL", None) + else: + os.environ["DELIVERY_DESK_BASE_URL"] = previous_base_url + + skill_text = (SCRIPT.parents[1] / "SKILL.md").read_text(encoding="utf-8") + assert "请回复“确认目标”" in skill_text + assert "Do not generate `plan-work` or `plan-round` before it." in skill_text + original_exact_project = MODULE.exact_project original_request_json = MODULE.request_json original_request_multipart = MODULE.request_multipart