From 0872ff6a20ab3be5d908cced12855f775f07803a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E9=BA=92?= Date: Fri, 28 Aug 2026 17:19:24 +0800 Subject: [PATCH] feat(cli): expose high-risk confirmation guidance in help and skills --- docs/agents/skill-change.md | 3 + .../cli/tests/skill-risk-confirmation.test.ts | 47 ++++++++ packages/runtime/src/registry.ts | 20 ++++ packages/runtime/tests/i18n.test.ts | 9 ++ packages/runtime/tests/registry-guard.test.ts | 8 +- skills/bailian-cli/SKILL.md | 51 +++++---- skills/bailian-cli/reference/knowledge.md | 107 ++++++++++++------ skills/bailian-cli/reference/permission.md | 19 +++- skills/bailian-finetune/SKILL.md | 2 +- skills/bailian-managed-agent/SKILL.md | 17 +-- .../reference/managed-agent.md | 36 ++++-- skills/bailian-protocol/SKILL.md | 9 ++ .../assets/issue-reporting.md | 48 ++++---- tools/generate-reference.ts | 26 ++++- 14 files changed, 288 insertions(+), 114 deletions(-) create mode 100644 packages/cli/tests/skill-risk-confirmation.test.ts diff --git a/docs/agents/skill-change.md b/docs/agents/skill-change.md index a5b6de8..7a6acdf 100644 --- a/docs/agents/skill-change.md +++ b/docs/agents/skill-change.md @@ -40,6 +40,8 @@ bailian-gen bailian-finetune bailian-managed-agent bailian-web-search - [ ] **整包装齐**:安装/升级文案主推 `bl skill init`;业务 skill **不**声明 `companions` - [ ] **协议读取**:CRITICAL / references 可链 `../bailian-protocol/…`;若读不到 → 停止执行 `bl`,提示 `bl skill init` +- [ ] **高风险确认**:统一由 `bailian-protocol` 定义;reference / leaf help 以 `risk: high` 明示风险,业务 skill 不得引导 Agent 自动补 `--yes`。遇到 exit code 7 / `requires_confirmation` 时停止执行并请求确认;目标或范围变化后重新确认 +- [ ] **正常控制流**:`requires_confirmation` 不是 CLI bug,`assets/issue-reporting.md` 必须将 exit code 7 保持在 EXCLUDE 范围 - [ ] **软 hand-off**:兄弟业务 skill **只写 skill 名**;已安装则 Read,未安装则 `bl … --help` 或提示整包安装;**不要**把 `../bailian-gen/…` 等写成执行前提 - [ ] **Hub vs 领域**:`bailian-cli` 的「When to use which command」只列 hub 拥有的意图;媒体 / 精调 / managed-agent 各留 hand-off 行,**不抄**领域默认模型与子命令明细 - [ ] **渐进披露**:SKILL 写意图路由与领域硬规则;flags / usage / examples 以 `reference/` 或 `bl --help` 为准,表后保留「勿猜 flag」指向句 @@ -55,6 +57,7 @@ bailian-gen bailian-finetune bailian-managed-agent bailian-web-search - [ ] 新一级命令组归属领域时:改 `tools/generate-reference.ts` 的 `GROUP_OWNER_SKILL`,并更新**拥有方** skill 的路由表;hub 最多加一行 hand-off - [ ] 跑 `pnpm run sync:skill-assets`(或 commit 走 pre-commit),提交生成的 `reference/` 与 version 同步结果 +- [ ] 高风险命令生成的 reference 必须包含 `Risk` / `Risk message` 和简短 Agent safety 提示;带 `--yes` 的示例必须标注只能在确认后执行,不要手改生成物 - [ ] 默认模型若写在领域路由表(如 `bailian-gen`):与命令 default / [model-add-remove.md](model-add-remove.md) 一并核对 ## 完成后自查 diff --git a/packages/cli/tests/skill-risk-confirmation.test.ts b/packages/cli/tests/skill-risk-confirmation.test.ts new file mode 100644 index 0000000..a7bca90 --- /dev/null +++ b/packages/cli/tests/skill-risk-confirmation.test.ts @@ -0,0 +1,47 @@ +import { readFileSync, readdirSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { expect, test } from "vite-plus/test"; + +const repositoryRoot = join(dirname(fileURLToPath(import.meta.url)), "../../.."); +const skillsRoot = join(repositoryRoot, "skills"); + +test("every generated high-risk command reference requires user confirmation before --yes", () => { + let highRiskCommandCount = 0; + + for (const skillDirectory of readdirSync(skillsRoot, { withFileTypes: true })) { + if (!skillDirectory.isDirectory()) continue; + const referenceDirectory = join(skillsRoot, skillDirectory.name, "reference"); + + let referenceFiles: string[]; + try { + referenceFiles = readdirSync(referenceDirectory).filter( + (fileName) => fileName.endsWith(".md") && fileName !== "index.md", + ); + } catch { + continue; + } + + for (const referenceFile of referenceFiles) { + const markdown = readFileSync(join(referenceDirectory, referenceFile), "utf8"); + const commandSections = markdown.split(/(?=^### `bl )/m).slice(1); + + for (const commandSection of commandSections) { + if (!commandSection.includes("`--yes`")) continue; + highRiskCommandCount += 1; + expect(commandSection).toMatch(/\|\s+\*\*Risk\*\*\s+\|\s+`high`\s+\|/); + expect(commandSection).toMatch(/\|\s+\*\*Risk message\*\*\s+\|\s+.+\|/); + expect(commandSection).toMatch(/type=.*requires_confirmation/); + const agentSafetyLine = commandSection + .split("\n") + .find((line) => line.startsWith("> **Agent safety:**")); + expect(agentSafetyLine).toBeDefined(); + expect(agentSafetyLine).toMatch(/never add `--yes` automatically/i); + expect(agentSafetyLine).toMatch(/explicit user confirmation/i); + expect(agentSafetyLine).not.toContain("`--dry-run`"); + } + } + } + + expect(highRiskCommandCount).toBeGreaterThan(0); +}); diff --git a/packages/runtime/src/registry.ts b/packages/runtime/src/registry.ts index 4b2fbcf..eb3260e 100644 --- a/packages/runtime/src/registry.ts +++ b/packages/runtime/src/registry.ts @@ -1,6 +1,7 @@ import type { AnyCommand, AuthRequirement, + CommandRiskLevel, FlagDef, FlagsDef, LocalizedText, @@ -41,10 +42,16 @@ const AUTH_LABELS = { none: { "en-US": "No Auth", "zh-CN": "无需鉴权" }, } satisfies Record; +const RISK_LEVEL_LABELS = { + high: { "en-US": "high", "zh-CN": "高风险" }, +} satisfies Record; + const HELP_TEXT = { usage: { "en-US": "Usage:", "zh-CN": "用法:" }, commands: { "en-US": "Commands:", "zh-CN": "命令:" }, authentication: { "en-US": "Authentication:", "zh-CN": "鉴权方式:" }, + risk: { "en-US": "Risk:", "zh-CN": "风险等级:" }, + riskMessage: { "en-US": "Risk message:", "zh-CN": "风险说明:" }, flags: { "en-US": "Flags:", "zh-CN": "选项:" }, globalFlags: { "en-US": "Global Flags:", "zh-CN": "全局选项:" }, modelAuthFlags: { "en-US": "Model Auth Flags:", "zh-CN": "模型鉴权选项:" }, @@ -64,6 +71,10 @@ const HELP_TEXT = { }, notes: { "en-US": "Notes:", "zh-CN": "说明:" }, examples: { "en-US": "Examples:", "zh-CN": "示例:" }, + confirmedExample: { + "en-US": "# Only after explicit confirmation:", + "zh-CN": "# 仅在明确确认后执行:", + }, minimalWorkflow: { "en-US": "Minimal workflow.yaml:", "zh-CN": "最小 workflow.yaml:" }, tryIt: { "en-US": "Try it:", "zh-CN": "试一试:" }, } satisfies Record; @@ -432,6 +443,12 @@ ${authFlagSections ? `${authFlagSections}\n\n` : ""}${b(this.localize(HELP_TEXT. out.write( `${b(this.localize(HELP_TEXT.authentication))} ${a(this.localize(AUTH_LABELS[cmd.auth]))}\n`, ); + if (cmd.risk !== undefined) { + out.write( + `${b(this.localize(HELP_TEXT.risk))} ${a(this.localize(RISK_LEVEL_LABELS[cmd.risk.level]))}\n`, + ); + out.write(`${b(this.localize(HELP_TEXT.riskMessage))} ${this.localize(cmd.risk.message)}\n`); + } const flagEntries = [ ...Object.entries(cmd.flags ?? {}), ...Object.entries(confirmationFlagDefs(cmd)), @@ -460,6 +477,9 @@ ${authFlagSections ? `${authFlagSections}\n\n` : ""}${b(this.localize(HELP_TEXT. out.write(`\n${b(this.localize(HELP_TEXT.examples))}\n`); for (const example of cmd.exampleArgs) { const localizedExample = this.localize(example); + if (cmd.risk !== undefined && /(?:^|\s)--yes(?:\s|$)/.test(localizedExample)) { + out.write(` ${d(this.localize(HELP_TEXT.confirmedExample))}\n`); + } const line = localizedExample.startsWith("#") ? localizedExample : localizedExample diff --git a/packages/runtime/tests/i18n.test.ts b/packages/runtime/tests/i18n.test.ts index ed1397d..2fffb7d 100644 --- a/packages/runtime/tests/i18n.test.ts +++ b/packages/runtime/tests/i18n.test.ts @@ -47,6 +47,13 @@ test("registry renders runtime help copy with the selected language", async () = }, ], auth: "none", + risk: { + level: "high", + message: { + "en-US": "This operation is permanent.", + "zh-CN": "该操作无法撤销。", + }, + }, run: async () => {}, }); const registry = new CommandRegistry({ test: command }, "bl", translator); @@ -69,6 +76,8 @@ test("registry renders runtime help copy with the selected language", async () = output = ""; registry.printHelp(["test"], stream); + expect(output).toContain("风险等级: 高风险"); + expect(output).toContain("风险说明: 该操作无法撤销。"); expect(output).toContain("测试说明"); expect(output).toContain('bl test --message "你好"'); expect(output).toContain(" # 流式输出响应"); diff --git a/packages/runtime/tests/registry-guard.test.ts b/packages/runtime/tests/registry-guard.test.ts index 87c1e21..1c1918d 100644 --- a/packages/runtime/tests/registry-guard.test.ts +++ b/packages/runtime/tests/registry-guard.test.ts @@ -53,11 +53,12 @@ test("high risk 命令不能自行声明 runtime 保留的 yes", () => { expect(() => new CommandRegistry({ "x normal": normal }, "bl")).not.toThrow(); }); -test("命令 help 只为 high risk 展示 runtime 注入的 --yes", () => { +test("命令 help 只为 high risk 展示风险信息和 runtime 注入的 --yes", () => { const high = defineCommand({ description: "danger", auth: "none", risk: { level: "high", message: "dangerous operation" }, + exampleArgs: ["--dry-run", "--yes"], run: noopRun, }); const normal = defineCommand({ @@ -77,5 +78,10 @@ test("命令 help 只为 high risk 展示 runtime 注入的 --yes", () => { } as unknown as NodeJS.WriteStream); expect(highHelp).toContain("--yes"); + expect(highHelp).toContain("Risk: high"); + expect(highHelp).toContain("Risk message: dangerous operation"); + expect(highHelp).toMatch(/# Only after explicit confirmation:\n\s+bl asset delete --yes/); expect(normalHelp).not.toContain("--yes"); + expect(normalHelp).not.toContain("Risk:"); + expect(normalHelp).not.toContain("Risk message:"); }); diff --git a/skills/bailian-cli/SKILL.md b/skills/bailian-cli/SKILL.md index b1a8d13..17761a2 100644 --- a/skills/bailian-cli/SKILL.md +++ b/skills/bailian-cli/SKILL.md @@ -56,30 +56,30 @@ Do not guess flags — use the reference files or `--help`. Use this table only after the decision table in [`bailian-protocol`](../bailian-protocol/SKILL.md#provider-selection-and-consent) has routed the request to `bl` (class 4, or class 2 after the user picks Bailian). Hub-owned intents only — for media / fine-tune / agents.yaml, soft hand-off to the domain skill. -| User intent | Command | Notes | -| ------------------------------------------------ | --------------------------------------------- | -------------------------------------------------------------------------------- | -| Explicit Bailian model chat / text execution | `bl text chat` | Default `qwen3.8-max` | -| Bailian agent / workflow | `bl app call` | Needs `--app-id` | -| Find app by name | `bl app list` then `bl app call` | Console auth | -| Bailian app memory CRUD (not host-agent memory) | `bl memory *` | [`reference/memory.md`](reference/memory.md) | -| Bailian knowledge base RAG | `bl knowledge search` / `chat` | API key + agent/workspace IDs | -| Upload a file as a step of a Bailian workflow | `bl file upload` | When you need `oss://` URL explicitly; not for generic hosting | -| Bailian model selection / recommendation | `bl advisor recommend` | Intent → candidate recall → LLM ranking | -| Bailian model catalog / pricing / params | `bl model list` | Console auth; `--model ` for detail, `--enrich` for input params | -| Install / list / update / remove registry skills | `bl skill add` / `list` / `update` / `remove` | Bailian skill registry; see [`reference/skill.md`](reference/skill.md) | -| Bailian MCP marketplace discovery / call | `bl mcp list` / `tools` / `call` | — | -| Bailian pipeline workflow (a step in a bl flow) | `bl pipeline run` / `validate` | JSON/YAML workflow definitions | -| Bailian rate limits / quota | `bl quota list` / `check` / `request` | Console auth; class 2 — ask which product first if unnamed | -| Bailian free tier / usage stats | `bl usage free` / `stats` / `freetier` | Console auth; class 2 — ask which product first if unnamed | -| Bailian Token Plan quota usage | `bl usage token-plan` | Console auth; class 2 — ask which product first if unnamed | -| Bailian Coding Plan quota usage | `bl usage coding-plan` | Console auth; class 2 — ask which product first if unnamed | -| Console API (advanced) | `bl console call` | Console auth | -| Bailian workspace listing | `bl workspace list` | Console auth | -| Switch CLI Help / Quick Start language | `bl config set --key language --value zh-CN` | Use `en-US` to switch back; follows the active config profile | -| Image / video / speech / omni / vision | → skill `bailian-gen` | Fallback: `bl image\|video\|speech\|omni\|vision --help` | -| Dataset / fine-tune / deploy | → skill `bailian-finetune` | Fallback: `bl dataset\|finetune\|deploy --help` | -| agents.yaml IaC / managed-agent sessions | → skill `bailian-managed-agent` | Fallback: `bl managed-agent --help`; `apply`/`destroy` need `--yes` after `plan` | -| Web search (model-aware routing) | → skill `bailian-web-search` | Token Plan vs MCP path + fallback; fallback: `bl search web --help` | +| User intent | Command | Notes | +| ------------------------------------------------ | --------------------------------------------- | -------------------------------------------------------------------------- | +| Explicit Bailian model chat / text execution | `bl text chat` | Default `qwen3.8-max` | +| Bailian agent / workflow | `bl app call` | Needs `--app-id` | +| Find app by name | `bl app list` then `bl app call` | Console auth | +| Bailian app memory CRUD (not host-agent memory) | `bl memory *` | [`reference/memory.md`](reference/memory.md) | +| Bailian knowledge base RAG | `bl knowledge search` / `chat` | API key + agent/workspace IDs | +| Upload a file as a step of a Bailian workflow | `bl file upload` | When you need `oss://` URL explicitly; not for generic hosting | +| Bailian model selection / recommendation | `bl advisor recommend` | Intent → candidate recall → LLM ranking | +| Bailian model catalog / pricing / params | `bl model list` | Console auth; `--model ` for detail, `--enrich` for input params | +| Install / list / update / remove registry skills | `bl skill add` / `list` / `update` / `remove` | Bailian skill registry; see [`reference/skill.md`](reference/skill.md) | +| Bailian MCP marketplace discovery / call | `bl mcp list` / `tools` / `call` | — | +| Bailian pipeline workflow (a step in a bl flow) | `bl pipeline run` / `validate` | JSON/YAML workflow definitions | +| Bailian rate limits / quota | `bl quota list` / `check` / `request` | Console auth; class 2 — ask which product first if unnamed | +| Bailian free tier / usage stats | `bl usage free` / `stats` / `freetier` | Console auth; class 2 — ask which product first if unnamed | +| Bailian Token Plan quota usage | `bl usage token-plan` | Console auth; class 2 — ask which product first if unnamed | +| Bailian Coding Plan quota usage | `bl usage coding-plan` | Console auth; class 2 — ask which product first if unnamed | +| Console API (advanced) | `bl console call` | Console auth | +| Bailian workspace listing | `bl workspace list` | Console auth | +| Switch CLI Help / Quick Start language | `bl config set --key language --value zh-CN` | Use `en-US` to switch back; follows the active config profile | +| Image / video / speech / omni / vision | → skill `bailian-gen` | Fallback: `bl image\|video\|speech\|omni\|vision --help` | +| Dataset / fine-tune / deploy | → skill `bailian-finetune` | Fallback: `bl dataset\|finetune\|deploy --help` | +| agents.yaml IaC / managed-agent sessions | → skill `bailian-managed-agent` | Fallback: `bl managed-agent --help`; `apply`/`destroy` also require `plan` | +| Web search (model-aware routing) | → skill `bailian-web-search` | Token Plan vs MCP path + fallback; fallback: `bl search web --help` | Flags, usage, and examples: see hub [`reference/`](reference/index.md) or `bl --help` — do not guess flags. Domain command details live in the owning skill's `reference/`. @@ -123,6 +123,7 @@ schema-export commands. - Usage / quota / credits questions that do not name a product → ask which product (Bailian or another AI service) first; run `bl usage` / `bl quota` only after the user picks Bailian or Bailian context is already established. - "Remember this" and memory requests default to the host agent's own memory; `bl memory *` is only for Bailian app memory resources. - `bl file upload` and `bl pipeline run` are steps inside a Bailian workflow; do not use them to capture generic "upload this file" or "run a pipeline" requests. -- `bl managed-agent apply` / `destroy` mutate remote resources and only execute with `--yes`; run `plan` first and show the diff before confirming a mutation. +- For `risk: high` commands or `requires_confirmation`, follow the shared protocol; never add `--yes` automatically. +- `bl managed-agent apply` / `destroy` have an additional domain rule: run `plan` first and show the diff before asking for confirmation. - When a matched `bl` command accepts a file URL, pass local paths directly; never require the user to host the file first. - Console login → always `--console-site domestic|international`; see [`../bailian-protocol/assets/setup.md`](../bailian-protocol/assets/setup.md#console-site-selection). diff --git a/skills/bailian-cli/reference/knowledge.md b/skills/bailian-cli/reference/knowledge.md index 44056a4..8e49e99 100644 --- a/skills/bailian-cli/reference/knowledge.md +++ b/skills/bailian-cli/reference/knowledge.md @@ -82,12 +82,16 @@ bl knowledge category add --name sub --parent-id cate-xxx ### `bl knowledge category delete` -| Field | Value | -| ------------------ | --------------------------------------------------------- | -| **Name** | `knowledge category delete` | -| **Description** | Delete a data-center category | -| **Authentication** | API Key | -| **Usage** | `bl knowledge category delete --category-id [flags]` | +| Field | Value | +| ------------------ | -------------------------------------------------------------------- | +| **Name** | `knowledge category delete` | +| **Description** | Delete a data-center category | +| **Authentication** | API Key | +| **Usage** | `bl knowledge category delete --category-id [flags]` | +| **Risk** | `high` | +| **Risk message** | This deletes the selected data-center category and cannot be undone. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -110,6 +114,7 @@ bl knowledge category delete --category-id cate-xxx --workspace-id ws-xxx ``` ```bash +# Only after explicit user confirmation: bl knowledge category delete --category-id cate-xxx --yes ``` @@ -248,6 +253,10 @@ bl knowledge chunk add --index-id idx-xxx --field columnA=v1 --field columnB=v2 | **Description** | Delete chunks from a knowledge base (irreversible) | | **Authentication** | API Key | | **Usage** | `bl knowledge chunk delete --index-id --chunk-id [flags]` | +| **Risk** | `high` | +| **Risk message** | This permanently deletes the selected chunks and cannot be undone. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -271,6 +280,7 @@ bl knowledge chunk delete --index-id idx-xxx --chunk-id chunk-a --chunk-id chunk ``` ```bash +# Only after explicit user confirmation: bl knowledge chunk delete --index-id idx-xxx --chunk-id chunk-a --yes ``` @@ -461,12 +471,16 @@ bl knowledge create --name demo --description 'product docs' --category-id cate- ### `bl knowledge delete` -| Field | Value | -| ------------------ | --------------------------------------------------------- | -| **Name** | `knowledge delete` | -| **Description** | Delete a knowledge base with all its documents and chunks | -| **Authentication** | API Key | -| **Usage** | `bl knowledge delete --index-id [flags]` | +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------------------------- | +| **Name** | `knowledge delete` | +| **Description** | Delete a knowledge base with all its documents and chunks | +| **Authentication** | API Key | +| **Usage** | `bl knowledge delete --index-id [flags]` | +| **Risk** | `high` | +| **Risk message** | This permanently deletes the knowledge base and all of its documents and chunks. Data-center files are not deleted. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -490,17 +504,22 @@ bl knowledge delete --index-id idx-xxx --workspace-id ws-xxx ``` ```bash +# Only after explicit user confirmation: bl knowledge delete --index-id idx-xxx --yes ``` ### `bl knowledge doc delete` -| Field | Value | -| ------------------ | --------------------------------------------------------------- | -| **Name** | `knowledge doc delete` | -| **Description** | Delete documents and their chunks from a knowledge base | -| **Authentication** | API Key | -| **Usage** | `bl knowledge doc delete --index-id --doc-id [flags]` | +| Field | Value | +| ------------------ | ------------------------------------------------------------------------ | +| **Name** | `knowledge doc delete` | +| **Description** | Delete documents and their chunks from a knowledge base | +| **Authentication** | API Key | +| **Usage** | `bl knowledge doc delete --index-id --doc-id [flags]` | +| **Risk** | `high` | +| **Risk message** | This permanently deletes the selected documents and all of their chunks. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -527,6 +546,7 @@ bl knowledge doc delete --index-id idx-xxx --doc-id file-xxx --workspace-id ws-x ``` ```bash +# Only after explicit user confirmation: bl knowledge doc delete --index-id idx-xxx --doc-id file-a --doc-id file-b --yes ``` @@ -728,12 +748,16 @@ bl knowledge doc upload --file ./docs/ --dry-run --verbose ### `bl knowledge file delete` -| Field | Value | -| ------------------ | ------------------------------------------------- | -| **Name** | `knowledge file delete` | -| **Description** | Permanently delete a file from the data center | -| **Authentication** | API Key | -| **Usage** | `bl knowledge file delete --file-id [flags]` | +| Field | Value | +| ------------------ | -------------------------------------------------------------------------------------------------------------------- | +| **Name** | `knowledge file delete` | +| **Description** | Permanently delete a file from the data center | +| **Authentication** | API Key | +| **Usage** | `bl knowledge file delete --file-id [flags]` | +| **Risk** | `high` | +| **Risk message** | This permanently deletes the data-center file. Knowledge-base document indexes that reference it may become invalid. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -757,6 +781,7 @@ bl knowledge file delete --file-id file-xxx --workspace-id ws-xxx ``` ```bash +# Only after explicit user confirmation: bl knowledge file delete --file-id file-xxx --yes ``` @@ -1032,12 +1057,16 @@ bl knowledge service create --name my-search --scene search --index-id idx-xxx ### `bl knowledge service delete` -| Field | Value | -| ------------------ | ---------------------------------------------------------- | -| **Name** | `knowledge service delete` | -| **Description** | Delete a retrieval / Q&A service (soft delete, idempotent) | -| **Authentication** | API Key | -| **Usage** | `bl knowledge service delete --agent-id [flags]` | +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------- | +| **Name** | `knowledge service delete` | +| **Description** | Delete a retrieval / Q&A service (soft delete, idempotent) | +| **Authentication** | API Key | +| **Usage** | `bl knowledge service delete --agent-id [flags]` | +| **Risk** | `high` | +| **Risk message** | This deletes the service and makes its agent ID unavailable for search and chat calls. The operation cannot be undone. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -1062,17 +1091,22 @@ bl knowledge service delete --agent-id aid-xxx --workspace-id ws-xxx ``` ```bash +# Only after explicit user confirmation: bl knowledge service delete --agent-id aid-xxx --yes ``` ### `bl knowledge service deploy` -| Field | Value | -| ------------------ | ----------------------------------------------------- | -| **Name** | `knowledge service deploy` | -| **Description** | Publish the beta draft of a service as a new version | -| **Authentication** | API Key | -| **Usage** | `bl knowledge service deploy --agent-id [flags]` | +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------ | +| **Name** | `knowledge service deploy` | +| **Description** | Publish the beta draft of a service as a new version | +| **Authentication** | API Key | +| **Usage** | `bl knowledge service deploy --agent-id [flags]` | +| **Risk** | `high` | +| **Risk message** | This publishes the current draft as a new version and changes the behavior seen by live callers. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -1098,6 +1132,7 @@ bl knowledge service deploy --agent-id aid-xxx --workspace-id ws-xxx ``` ```bash +# Only after explicit user confirmation: bl knowledge service deploy --agent-id aid-xxx --version-desc 'tuned rerank params' --yes ``` diff --git a/skills/bailian-cli/reference/permission.md b/skills/bailian-cli/reference/permission.md index 776f60f..54e04ab 100644 --- a/skills/bailian-cli/reference/permission.md +++ b/skills/bailian-cli/reference/permission.md @@ -109,12 +109,16 @@ bl permission list --output text ### `bl permission revoke` -| Field | Value | -| ------------------ | ----------------------------------------------------------------------------- | -| **Name** | `permission revoke` | -| **Description** | Revoke model permissions (inference / finetune / deploy) | -| **Authentication** | API Key | -| **Usage** | `bl permission revoke --model [--action ] \| --all [flags]` | +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Name** | `permission revoke` | +| **Description** | Revoke model permissions (inference / finetune / deploy) | +| **Authentication** | API Key | +| **Usage** | `bl permission revoke --model [--action ] \| --all [flags]` | +| **Risk** | `high` | +| **Risk message** | This revokes model permissions and may interrupt inference, fine-tuning, or deployment workloads. With --all, it also clears all historical inference grants. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -137,14 +141,17 @@ bl permission list --output text #### Examples ```bash +# Only after explicit user confirmation: bl permission revoke --model qwen-plus --yes ``` ```bash +# Only after explicit user confirmation: bl permission revoke --model qwen-plus,qwen3-max --action inference,finetune --yes ``` ```bash +# Only after explicit user confirmation: bl permission revoke --all --yes ``` diff --git a/skills/bailian-finetune/SKILL.md b/skills/bailian-finetune/SKILL.md index 30339f1..b1e0e65 100644 --- a/skills/bailian-finetune/SKILL.md +++ b/skills/bailian-finetune/SKILL.md @@ -33,7 +33,7 @@ description: >- - Unsure which training methods a base model supports → `bl finetune capability --base-model ` or `--training-type sft|sft-lora|dpo|cpt`. - Text `--training-type` values: `sft` / `sft-lora` / `dpo` / `dpo-lora` / `cpt`. Audio bases include `cosyvoice-v3-flash`; image bases include `wan2.7-image-pro`. - Deployment plans: audio defaults to `--plan mu`; text/image default to `lora`. -- Preview write operations (create / delete / cancel / scale) with `--dry-run` first, and confirm with the user before deleting a job or dataset. +- For `risk: high` or `requires_confirmation`, follow `bailian-protocol`; never add `--yes` automatically. ## When to use which command diff --git a/skills/bailian-managed-agent/SKILL.md b/skills/bailian-managed-agent/SKILL.md index 7277707..ce90163 100644 --- a/skills/bailian-managed-agent/SKILL.md +++ b/skills/bailian-managed-agent/SKILL.md @@ -8,7 +8,7 @@ description: >- 阿里云百炼托管 Agent 声明式基础设施入口:用户要创建agent、初始化 agents.yaml、校验或预览 agent 配置变更、 创建/更新/销毁百炼托管 Agent 或 Deployment、和托管 agent 对话、查会话事件历史、导入或取消跟踪远端资源时使用 `bl managed-agent`。以 agents.yaml 为唯一事实源做 IaC:init 建脚手架、validate 离线校验、plan 预览 diff、 - apply / destroy 变更远端资源且必须带 `--yes`,务必先 plan 给用户看 diff 再让其确认。 + apply / destroy 变更远端资源且受统一高风险确认闸门保护,务必先 plan 给用户看 diff 再让其确认,禁止自动添加 `--yes`。 反触发:调用已上线的百炼应用/智能体走 bailian-app-call 或 `bl app`;宿主 agent 自身的记忆、技能、 子代理不走本 skill;生图生视频走 bailian-gen。 官方安装:`bl skill init`(与共享协议 bailian-protocol 同装)。 @@ -16,15 +16,17 @@ description: >- # Bailian managed agent IaC (`bl managed-agent`) -**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Version & updates (pre-flight checklist) and CLI errors: report an issue. Command details are authoritative in [`reference/managed-agent.md`](reference/managed-agent.md) and `bl managed-agent --help` — do not guess flags. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** +**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): High-risk operation confirmation, Version & updates (pre-flight checklist), and CLI errors: report an issue. Command details are authoritative in [`reference/managed-agent.md`](reference/managed-agent.md) and `bl managed-agent --help` — do not guess flags. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** ## Safety guardrail (the most important rule) -`apply` / `destroy` **mutate remote resources** and only execute when `--yes` is passed: +`apply` / `destroy` **mutate remote resources** and add a domain-specific preview requirement on top of the shared high-risk confirmation protocol: 1. Always run `bl managed-agent plan` first and show the diff to the user. -2. Only after explicit user confirmation, retry `apply` / `destroy` with `--yes`. -3. Never add `--yes` on your own initiative before the user has confirmed. +2. Ask the user to confirm the exact action and scope shown in the plan. +3. Only then run `apply` / `destroy` with `--yes`; a changed plan requires confirmation again. + +`session delete` and future `risk: high` commands follow the shared protocol. ## IaC lifecycle @@ -32,8 +34,9 @@ description: >- 1. Init bl managed-agent init # scaffold agents.yaml 2. Validate bl managed-agent validate # offline, no network calls 3. Preview bl managed-agent plan # show the pending change diff -4. Apply bl managed-agent apply --yes # only after user confirmation -5. Destroy bl managed-agent destroy --yes # only after user confirmation +4. Confirm show the plan and ask the user # no automatic --yes +5. Apply bl managed-agent apply --yes # only after explicit confirmation +6. Destroy bl managed-agent destroy --yes # separate explicit confirmation ``` ## Deployment as IaC diff --git a/skills/bailian-managed-agent/reference/managed-agent.md b/skills/bailian-managed-agent/reference/managed-agent.md index e5ce1b7..5e96846 100644 --- a/skills/bailian-managed-agent/reference/managed-agent.md +++ b/skills/bailian-managed-agent/reference/managed-agent.md @@ -31,12 +31,16 @@ Index: [index.md](index.md) ### `bl managed-agent apply` -| Field | Value | -| ------------------ | -------------------------------------------------------------------------------- | -| **Name** | `managed-agent apply` | -| **Description** | Apply planned changes to create/update/delete agent resources | -| **Authentication** | API Key | -| **Usage** | `bl managed-agent apply [--file ] [--provider ] [--concurrency ]` | +| Field | Value | +| ------------------ | ----------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent apply` | +| **Description** | Apply planned changes to create/update/delete agent resources | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent apply [--file ] [--provider ] [--concurrency ]` | +| **Risk** | `high` | +| **Risk message** | This applies the current plan and may create, update, or delete remote managed Agent resources. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -59,21 +63,27 @@ Index: [index.md](index.md) #### Examples ```bash +# Only after explicit user confirmation: bl managed-agent apply --yes ``` ```bash +# Only after explicit user confirmation: bl managed-agent apply --provider bailian --yes ``` ### `bl managed-agent destroy` -| Field | Value | -| ------------------ | ------------------------------------------------------ | -| **Name** | `managed-agent destroy` | -| **Description** | Destroy all managed agent resources tracked in state | -| **Authentication** | API Key | -| **Usage** | `bl managed-agent destroy [--file ] [--cascade]` | +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent destroy` | +| **Description** | Destroy all managed agent resources tracked in state | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent destroy [--file ] [--cascade]` | +| **Risk** | `high` | +| **Risk message** | This deletes every managed Agent resource tracked in state; --cascade may also delete dependent resources. | + +> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope. #### Flags @@ -94,10 +104,12 @@ bl managed-agent apply --provider bailian --yes #### Examples ```bash +# Only after explicit user confirmation: bl managed-agent destroy --yes ``` ```bash +# Only after explicit user confirmation: bl managed-agent destroy --yes --cascade ``` diff --git a/skills/bailian-protocol/SKILL.md b/skills/bailian-protocol/SKILL.md index 541a848..c0c3b1a 100644 --- a/skills/bailian-protocol/SKILL.md +++ b/skills/bailian-protocol/SKILL.md @@ -41,6 +41,15 @@ Ask templates for classes 2 and 3 (match the user's language): After approval, treat Bailian as selected for the current task. Do not ask again for intermediate commands, polling, downloads, retries, or related follow-ups. Ask again only if the scope changes materially, such as a substantially larger cost or a destructive operation. +## High-risk operation confirmation (mandatory) + +`risk: high` in a command reference or leaf `--help` marks a high-risk operation. For older CLI output without this field, treat `--yes` as the conservative fallback. Exit code **7** with `error.type: "requires_confirmation"` is an expected stop signal, not a CLI bug. + +- Never add `--yes` automatically. +- Show the risk message and a safe summary of the action, target, and scope without exposing credentials, then ask for explicit confirmation. +- Only after confirmation, re-run the same operation with `--yes`. Any material change to the scope requires confirmation again. +- If the user declines or does not answer, stop. + ## Family routing & hand-offs 业务路由(**软 hand-off**:按 skill **名**路由;已安装则 Read 其 `SKILL.md`,未安装则用 `bl --help`,或提示整包安装 diff --git a/skills/bailian-protocol/assets/issue-reporting.md b/skills/bailian-protocol/assets/issue-reporting.md index b1df4d5..28e9fac 100644 --- a/skills/bailian-protocol/assets/issue-reporting.md +++ b/skills/bailian-protocol/assets/issue-reporting.md @@ -15,7 +15,7 @@ When `bl` fails, the agent first helps the user fix the problem. If the failure function shouldOfferIssueReport(exitCode, apiCode, message, hint): # Step 1: Unambiguous EXCLUDE by exit code - if exitCode in [2 (USAGE), 3 (AUTH), 4 (QUOTA), 10 (CONTENT_FILTER)]: + if exitCode in [2 (USAGE), 3 (AUTH), 4 (QUOTA), 7 (CONFIRMATION_REQUIRED), 10 (CONTENT_FILTER)]: return EXCLUDE # help user fix; never offer reporting # Step 2: NETWORK / TIMEOUT — exclude if hint is actionable @@ -69,18 +69,19 @@ function matchesIncludeCriteria(exitCode, apiCode, message): These are **user**, **environment**, or **service business** errors. Give fix hints; do not ask to file an issue. -| Category | Signal | Examples | -| -------------------------- | -------------------------------------- | ------------------------------------------------------------------------------- | -| **Usage / args** | Exit code **2** (USAGE) | Missing flag, invalid path, unknown subcommand, local file not found | -| **Auth** | Exit code **3** (AUTH) | No API key, invalid key, expired console token | -| **Quota** | Exit code **4** (QUOTA) | Free tier exhausted, rate limit / quota messages | -| **Content filter** | Exit code **10** (CONTENT_FILTER) | Content moderation blocked the request | -| **Model not found** | Message or `api_code` | `ModelNotFound`, `invalid_request_error` naming a bad model, HTTP 404 for model | -| **Invalid API params** | USAGE or service validation | `InvalidParameter`, `invalid_request_error` for bad `--size`, `--format`, etc. | -| **Free quota query** | `bl usage free` business result | Quota used up — not a CLI defect | -| **Obvious local env** | Hint is sufficient | `ENOENT` / `EACCES`, wrong file path, disk full | -| **Network (self-service)** | Exit code **6** (NETWORK) + clear hint | DNS, proxy, TLS — user fixes `DASHSCOPE_BASE_URL`, proxy, or network | -| **Timeout (self-service)** | Exit code **5** (TIMEOUT) + hint works | Increase `--timeout`, check `base_url` with `bl auth status` | +| Category | Signal | Examples | +| -------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------- | +| **Usage / args** | Exit code **2** (USAGE) | Missing flag, invalid path, unknown subcommand, local file not found | +| **Auth** | Exit code **3** (AUTH) | No API key, invalid key, expired console token | +| **Quota** | Exit code **4** (QUOTA) | Free tier exhausted, rate limit / quota messages | +| **Confirmation required** | Exit code **7** + `requires_confirmation` | Expected high-risk control flow; ask the user, never auto-retry with `--yes` | +| **Content filter** | Exit code **10** (CONTENT_FILTER) | Content moderation blocked the request | +| **Model not found** | Message or `api_code` | `ModelNotFound`, `invalid_request_error` naming a bad model, HTTP 404 for model | +| **Invalid API params** | USAGE or service validation | `InvalidParameter`, `invalid_request_error` for bad `--size`, `--format`, etc. | +| **Free quota query** | `bl usage free` business result | Quota used up — not a CLI defect | +| **Obvious local env** | Hint is sufficient | `ENOENT` / `EACCES`, wrong file path, disk full | +| **Network (self-service)** | Exit code **6** (NETWORK) + clear hint | DNS, proxy, TLS — user fixes `DASHSCOPE_BASE_URL`, proxy, or network | +| **Timeout (self-service)** | Exit code **5** (TIMEOUT) + hint works | Increase `--timeout`, check `base_url` with `bl auth status` | **Rule:** If the authoritative source of the error is the **service response** or **user input**, treat it as non-reportable (same boundary as the CLI repo’s error-handling docs). @@ -337,15 +338,16 @@ Do **not** block on `gh` — always provide a manual path. ## Exit codes (reference) -| Code | Name | Usually reportable? | -| ---- | -------------- | ----------------------------------------------- | -| 0 | SUCCESS | — | -| 1 | GENERAL | Sometimes (if CLI bug, not service passthrough) | -| 2 | USAGE | No | -| 3 | AUTH | No | -| 4 | QUOTA | No | -| 5 | TIMEOUT | Rarely (after user fixes env) | -| 6 | NETWORK | Rarely (after user fixes env) | -| 10 | CONTENT_FILTER | No | +| Code | Name | Usually reportable? | +| ---- | --------------------- | ----------------------------------------------- | +| 0 | SUCCESS | — | +| 1 | GENERAL | Sometimes (if CLI bug, not service passthrough) | +| 2 | USAGE | No | +| 3 | AUTH | No | +| 4 | QUOTA | No | +| 5 | TIMEOUT | Rarely (after user fixes env) | +| 6 | NETWORK | Rarely (after user fixes env) | +| 7 | CONFIRMATION_REQUIRED | No — expected high-risk control flow | +| 10 | CONTENT_FILTER | No | JSON errors use the same numeric `error.code` field when `--output json` is set. diff --git a/tools/generate-reference.ts b/tools/generate-reference.ts index d1ad007..2565392 100644 --- a/tools/generate-reference.ts +++ b/tools/generate-reference.ts @@ -128,7 +128,11 @@ function formatFlagsTable(flags: FlagsDef | undefined): string { ].join("\n"); } -function formatExamples(path: string, exampleArgs: LocalizedText[] | undefined): string { +function formatExamples( + path: string, + exampleArgs: LocalizedText[] | undefined, + highRisk: boolean, +): string { if (!exampleArgs?.length) return "_No examples._\n"; // Commands store argument-only examples; prepend `bl ` for the reference. return ( @@ -136,7 +140,11 @@ function formatExamples(path: string, exampleArgs: LocalizedText[] | undefined): .map((example) => { const text = referenceText(example); const line = text.startsWith("#") ? text : `bl ${path}${text ? ` ${text}` : ""}`; - return ["```bash", line, "```"].join("\n"); + const confirmationComment = + highRisk && /(?:^|\s)--yes(?:\s|$)/.test(text) + ? ["# Only after explicit user confirmation:"] + : []; + return ["```bash", ...confirmationComment, line, "```"].join("\n"); }) .join("\n\n") + "\n" ); @@ -157,8 +165,20 @@ function commandSection(path: string, cmd: AnyCommand): string { // Commands store argument-only usage; the `bl ` prefix is added here. const usage = `bl ${path}${cmd.usageArgs ? ` ${cmd.usageArgs}` : ""}`; lines.push(`| **Usage** | \`${escCell(usage)}\` |`); + if (cmd.risk !== undefined) { + lines.push(`| **Risk** | \`${cmd.risk.level}\` |`); + lines.push(`| **Risk message** | ${escCell(referenceText(cmd.risk.message))} |`); + } lines.push(""); + if (cmd.risk !== undefined) { + lines.push( + '> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, ' + + "stop and ask for explicit user confirmation of the same action and scope.", + "", + ); + } + // 与命令 help 的 Flags 区一致:自有 + 该命令可见的凭证域 flag。 lines.push("#### Flags", ""); lines.push( @@ -175,7 +195,7 @@ function commandSection(path: string, cmd: AnyCommand): string { } lines.push("#### Examples", ""); - lines.push(formatExamples(path, cmd.exampleArgs)); + lines.push(formatExamples(path, cmd.exampleArgs, cmd.risk !== undefined)); return lines.join("\n"); }