feat(skill): 固定服务地址并强化目标确认

This commit is contained in:
yuzhe
2026-07-23 13:50:24 +08:00
parent c067881415
commit 557e24883d
6 changed files with 26 additions and 8 deletions

View File

@@ -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: `本次操作目标是否为:运营组「<group_name>」(ID <group_id>) / 项目「<project_name>」(ID <project_id>, slug <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.

View File

@@ -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."

View File

@@ -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:

View File

@@ -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 后重新生成分发包:

View File

@@ -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