Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41136b6d41 | ||
|
|
746b7e403c | ||
|
|
86e34b5489 | ||
|
|
b64438d01c | ||
|
|
4943c6ff49 | ||
|
|
f0fef85905 | ||
|
|
7773eb27f1 | ||
|
|
18e32ad09d | ||
|
|
a9857d94d7 | ||
|
|
461455b4fa | ||
|
|
f0d558a0d1 | ||
|
|
3cf690e779 | ||
|
|
340f01e95c | ||
|
|
621229fca2 | ||
|
|
acfbd35aa2 | ||
|
|
95a5fd069a | ||
|
|
d6292d92d3 | ||
|
|
afb9c27560 | ||
|
|
08c9c8a923 | ||
|
|
f805c966d6 | ||
|
|
eae7955448 | ||
|
|
ce0501b93e | ||
|
|
da93fbcc47 | ||
|
|
92f1daa95f | ||
|
|
9911d8f9c9 | ||
|
|
52324e9bc2 | ||
|
|
a80ac662f5 | ||
|
|
a5cefd67f3 | ||
|
|
8c9c22f4b0 | ||
|
|
37a05db6e9 | ||
|
|
41fdf79aea | ||
|
|
ceca98c573 | ||
|
|
c84a42b0d5 | ||
|
|
fb7696293d | ||
|
|
93d6e1a001 | ||
|
|
4d4817d74f | ||
|
|
110780bf74 | ||
|
|
26b100c6bb |
@@ -21,6 +21,23 @@ primary paths, aliases, and navigation. Edit it only when reviewed exposure,
|
||||
identity, primary path, or aliases change; parameter, Skill, and metadata-only
|
||||
changes must not rewrite it mechanically.
|
||||
|
||||
## Command framework declaration
|
||||
|
||||
- Framework definition: `docs/rfc-command-framework-convergence.md` **§5.0**
|
||||
- Today: `helpers.LeafSpec` / `shortcut.Shortcut` → `corecmd.Spec` (+ optional `Schema`) → `corecmd.New`
|
||||
- **Declare = final Schema source**: `Flags` / `Constraints` / `Safety` / `ConstParams` / `Schema` (ToolSpec groups)
|
||||
- `Safety` uses the existing `cli.SafetySpec` directly. Its `confirmation` drives the runtime gate; `effect` / `risk` / `idempotency` are published unchanged. When `Schema` is set, convert once → `RegisterRuntimeContractFinal` (map store, no JSON/deep-clone); assembly **pass-throughs** Final.
|
||||
- **Execute** = hooks (`Validate` / `Call` / `RunE` / `PostMount`) — not a second surface authority
|
||||
- Declaration path has **no reviewed parallel fields**; migration-only `runtime_gate` annotate until `Safety` is declared
|
||||
|
||||
## flag / help / schema homology
|
||||
|
||||
- Decision (path A — Contract/LeafSpec is CLI-surface authority **and must embed into Schema**): `docs/flag-help-schema-homology.md`
|
||||
- Hard rule: every help/Schema fact is **declared** **or** **annotated**; never inference-only (§1.1–§1.3; framework §5.0).
|
||||
- Embed path: `corecmd.New` → `dws.schema.*` annotations → Schema catalog assembly
|
||||
- MCP metadata must not create CLI flags; optional 1:1 passthrough is a gated subset only.
|
||||
- Planned gate IDs: `HOM-P*`, `HOM-S*`, `HOM-I1`, `HOM-D1` (see that doc §4).
|
||||
|
||||
## Agent Schema contract
|
||||
|
||||
The Schema data flow is one way:
|
||||
|
||||
@@ -6,6 +6,10 @@ The format is inspired by [Keep a Changelog](https://keepachangelog.com/) and th
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Unified command safety and Shortcut runtime (H0)** — Shortcut leaves now execute through `corecmd.New`, sharing the same typed Safety confirmation gate as Leaf commands. EOF / closed stdin returns `confirmation_required`, and interactive `no` returns the existing non-zero cancellation validation error instead of reporting success for an operation that did not run. Pass `--yes` or `--dry-run` to skip the prompt.
|
||||
|
||||
## [1.0.56-beta.2] - 2026-07-30
|
||||
|
||||
This beta adds PRs #831 and #835 on top of v1.0.56-beta.1. It separates
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
- `internal/security`: endpoint allowlist and domain trust
|
||||
- `internal/safety`: runtime safety checks (confirm prompts, dry-run guards)
|
||||
- `internal/cobracmd`: shared Cobra command builders
|
||||
- `internal/corecmd`: dispatch-agnostic leaf-command base — flag registration,
|
||||
alias/env/default value resolution, required and cross-flag constraint
|
||||
validation, Risk write confirmation, toolArgs assembly, Runtime Schema
|
||||
projection. Distinct from `internal/cobracmd` (generic tree helpers): it owns
|
||||
the declarative leaf contract (`corecmd.Spec`) that the LeafSpec framework is
|
||||
built on and that the Shortcut adapter projects into.
|
||||
- `internal/pat`: PAT (Personal Access Token) authorization flow
|
||||
- `internal/output`: response formatting (json, table, raw, pretty)
|
||||
- `internal/logging`: structured logging and argument sanitization
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
# command 领域模型
|
||||
|
||||
本文档描述 `internal/corecmd` 包的领域模型——类型、概念及其关系。
|
||||
|
||||
## 核心模型图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ corecmd.Spec │
|
||||
│ (一个叶子命令的完整契约) │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─── CLI 表面 ───┐ ┌─── 参数声明 ───────────────────────────┐ │
|
||||
│ │ Use │ │ FlagSpec[] │ │
|
||||
│ │ Short │ │ ├─ Name / Kind / Default │ │
|
||||
│ │ Long │ │ ├─ Required / MarkRequired │ │
|
||||
│ │ Example │ │ ├─ Aliases[] / EnvVar (回退链) │ │
|
||||
│ └────────────────┘ │ ├─ Bind / Transform / OmitEmpty │ │
|
||||
│ │ └─ Enum / Format / SchemaDescription │ │
|
||||
│ │ │ │
|
||||
│ │ Constraint[] │ │
|
||||
│ │ ├─ at_least_one │ │
|
||||
│ │ ├─ exactly_one │ │
|
||||
│ │ └─ mutually_exclusive │ │
|
||||
│ │ │ │
|
||||
│ │ ConstParams map[string]any │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── 安全模型 ──────────────────────────────────────────────────┐ │
|
||||
│ │ Safety cli.SafetySpec │ │
|
||||
│ │ ├─ Effect (read / write / destructive) │ │
|
||||
│ │ ├─ Risk (low / medium / high) │ │
|
||||
│ │ ├─ Confirmation (not_required / user_required) ──▶ 运行时门 │ │
|
||||
│ │ └─ Idempotency (idempotent / retryable / …) │ │
|
||||
│ │ │ │
|
||||
│ │ 四字段彼此独立;同一值同时供运行时与 Schema 使用 │ │
|
||||
│ │ ConfirmFirst: bool (只控制确认门顺序) │ │
|
||||
│ └───────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Schema 声明 (Agent 可见的元数据) ──────────────────────────┐ │
|
||||
│ │ SchemaDecl │ │
|
||||
│ │ ├─ Title / Description │ │
|
||||
│ │ ├─ DryRunDecl {PreviewKind, RemoteReads} │ │
|
||||
│ │ ├─ InterfaceDecl {Mode, Availability, Reason, Ref} │ │
|
||||
│ │ ├─ SelectionDecl {AgentSummary, UseWhen, AvoidWhen, │ │
|
||||
│ │ │ Prerequisites, Tips, Examples} │ │
|
||||
│ │ ├─ IdentityDecl {ProductID, CanonicalPath, Aliases, ...} │ │
|
||||
│ │ └─ Positionals[] {Name, Type, Required, Variadic} │ │
|
||||
│ └───────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── 执行体 (恰好一个) ─────────────────────────────────────────┐ │
|
||||
│ │ Invoke(Ctx, toolArgs) ← 单步:框架装配好 args 后派发 │ │
|
||||
│ │ Orchestrate(Ctx) ← 多步:自行组装多次调用 │ │
|
||||
│ │ RunE(cmd, args) ← 逃生舱:完全自定义 │ │
|
||||
│ └───────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── 钩子 ─────────────────────────────────────────────────────┐ │
|
||||
│ │ Validate(cmd, args) ← 条件式业务校验(约束表达不了的) │ │
|
||||
│ │ PostMount(cmd) ← 挂载收尾(设置 Args 等 cobra 属性) │ │
|
||||
│ └───────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 构建与执行流
|
||||
|
||||
```
|
||||
corecmd.Spec ──── corecmd.New() ────▶ cobra.Command ──── 用户执行 ────▶ Ctx
|
||||
│ │ │
|
||||
构建时检查: 注册产物: 执行上下文:
|
||||
• validateDispatchDecl • Flags + Aliases • Str(name)
|
||||
• validateSafetySpec • Annotations (Schema) • Int(name)
|
||||
• validateSchemaDecl • Long (约束 help) • Bool(name)
|
||||
• RegisterFlags • RunE (管线) • StrSlice(name)
|
||||
• ValidateConstraintDecls • Changed(name)
|
||||
• embedContractIntoSchema • DryRun() / Yes()
|
||||
• AnnotateConstraints
|
||||
• PostMount
|
||||
```
|
||||
|
||||
## 领域概念
|
||||
|
||||
| 概念 | 类型 | 职责 |
|
||||
|------|------|------|
|
||||
| **corecmd.Spec** | struct | 一个命令的完整契约(声明 + 执行) |
|
||||
| **FlagSpec** | struct | 一个参数的注册、回退链、绑定规则 |
|
||||
| **Constraint** | struct | 参数间的关系约束 |
|
||||
| **Safety** | cli.SafetySpec | 运行时与 Schema 共用的安全契约 |
|
||||
| **SchemaDecl** | struct | Agent 可见的完整工具规格声明 |
|
||||
| **SelectionDecl** | struct | Agent 选择该工具的语义指引 |
|
||||
| **InterfaceDecl** | struct | 工具的接口模式与可用性 |
|
||||
| **DryRunDecl** | struct | dry-run 能力声明 |
|
||||
| **IdentityDecl** | struct | 工具在注册表中的身份标识 |
|
||||
| **PositionalDecl** | struct | 有序位置参数声明 |
|
||||
| **Ctx** | struct | 执行上下文(类型安全的 flag 读取) |
|
||||
| **New** | func | 统一构建器(`corecmd.Spec` → `*cobra.Command`) |
|
||||
|
||||
## SafetySpec
|
||||
|
||||
`corecmd.Spec.Safety` 直接复用 `cli.SafetySpec`,没有 command 自定义 Risk/Safety 枚举,也没有 `SafetyDecl` 覆盖层:
|
||||
|
||||
```go
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "write",
|
||||
Risk: "high",
|
||||
Confirmation: "user_required",
|
||||
Idempotency: "unknown",
|
||||
}
|
||||
```
|
||||
|
||||
- `Confirmation == "user_required"` 时执行确认;`--yes` 和 `--dry-run` 可跳过交互。
|
||||
- `Effect`、`Risk`、`Idempotency` 不参与确认决策,也不会改写 `Confirmation`。
|
||||
- 任意一个字段非空时,四个字段必须全部显式声明;构建时拒绝部分声明。
|
||||
- 完全空值仅作为历史只读默认,最终发布为 `read/low/not_required/idempotent`。
|
||||
|
||||
## FlagSpec 有效值回退链
|
||||
|
||||
框架统一的 flag 值解析顺序:
|
||||
|
||||
```
|
||||
显式主 flag (Changed)
|
||||
│ 空?
|
||||
▼
|
||||
隐藏别名 (Changed, 按声明序)
|
||||
│ 空?
|
||||
▼
|
||||
环境变量 (EnvVar)
|
||||
│ 空?
|
||||
▼
|
||||
注册默认值 (Default)
|
||||
│ 空?
|
||||
▼
|
||||
ArgDefault (兜底)
|
||||
```
|
||||
|
||||
各 Kind 的特殊行为:
|
||||
|
||||
| Kind | 入参条件 | 回退链 |
|
||||
|------|----------|--------|
|
||||
| KindString | 有效值非空(或 !OmitEmpty) | 完整参与 |
|
||||
| KindInt | 值 ≠ 0(putInt 语义) | 完整参与 |
|
||||
| KindBool | Changed 时入参(显式 false 也下发) | 不参与别名/env 回退 |
|
||||
| KindStringSlice | 存在非空元素 | 仅 Changed 的主 flag/alias |
|
||||
|
||||
## Constraint 约束
|
||||
|
||||
声明式跨 flag 关系,构建时校验合法性,运行时统一执行:
|
||||
|
||||
| Kind | 语义 | 错误文案示例 |
|
||||
|------|------|-------------|
|
||||
| `at_least_one` | 至少提供一个 | "请至少指定 --a、--b 之一" |
|
||||
| `exactly_one` | 恰好提供一个 | "请指定 --a、--b 之一" / "只能指定其一" |
|
||||
| `mutually_exclusive` | 最多提供一个 | "参数 --a、--b 互斥,只能指定其一" |
|
||||
|
||||
"是否提供"的判定复用有效值回退链(显式主 flag → 别名 → env),注册默认值不算作已提供。
|
||||
|
||||
## SchemaDecl 子结构
|
||||
|
||||
### SelectionDecl(Agent 选择指引)
|
||||
|
||||
```go
|
||||
SelectionDecl{
|
||||
AgentSummary: "一句话描述工具做什么",
|
||||
UseWhen: []string{"在什么场景下应该选择这个工具"},
|
||||
AvoidWhen: []string{"什么场景不应该用,应该用什么替代"},
|
||||
Prerequisites: []string{"使用前提条件"},
|
||||
Tips: []string{"使用技巧"},
|
||||
Examples: []string{"dws dev app create --name Bot --dry-run"},
|
||||
}
|
||||
```
|
||||
|
||||
### InterfaceDecl(接口模式)
|
||||
|
||||
```go
|
||||
InterfaceDecl{
|
||||
Mode: "composite", // local / pinned / composite
|
||||
Availability: "available", // available / unavailable
|
||||
Reason: "...", // composite/unavailable 时的原因
|
||||
ProductID: "...", // pinned 时的 ref
|
||||
RPCName: "...", // pinned 时的 ref
|
||||
}
|
||||
```
|
||||
|
||||
### DryRunDecl(dry-run 能力)
|
||||
|
||||
```go
|
||||
DryRunDecl{
|
||||
PreviewKind: "invocation", // invocation / request / plan
|
||||
RemoteReads: false, // dry-run 时是否发起远端读
|
||||
}
|
||||
```
|
||||
|
||||
## 执行体三选一
|
||||
|
||||
| 执行体 | 适用场景 | 框架做了什么 |
|
||||
|--------|----------|-------------|
|
||||
| **Invoke** | 单步 MCP/后端调用 | 框架完成 required→constraint→validate→buildArgs→confirm,传入装配好的 toolArgs |
|
||||
| **Orchestrate** | 多步编排 | 框架完成 required→constraint→validate→confirm,传入 Ctx 自行组装调用 |
|
||||
| **RunE** | 逃生舱 | 框架仍执行 Safety 确认,具体业务执行完全自定义 |
|
||||
|
||||
## 设计不变量
|
||||
|
||||
1. **一个 corecmd.Spec = 一个叶子命令的全部事实**
|
||||
2. **声明面绝不调用后端**——command 是 dispatch-agnostic
|
||||
3. **执行面绝不发明 CLI 表面**——业务 flag 必须在 Flags 声明
|
||||
4. **构建时拦截 > 运行时报错**——声明错误 panic 在注册阶段
|
||||
5. **SafetySpec 是单一事实源**——Confirmation 驱动运行时,其余字段原样进入 Schema
|
||||
6. **声明即 review**——代码中的 Schema 经 code review 后直接投影,不依赖外部 hint 文件
|
||||
@@ -0,0 +1,270 @@
|
||||
# 命令框架架构
|
||||
|
||||
本文档描述 `internal/corecmd` 统一命令框架的当前架构,面向框架使用者和维护者。
|
||||
|
||||
## 概览
|
||||
|
||||
```
|
||||
用户输入 → cobra 命令树 → corecmd.New() → 运行时管线 → 后端派发
|
||||
```
|
||||
|
||||
命令框架将 CLI 命令的**声明**与**执行**分离:
|
||||
|
||||
- **声明面** — 数据字段描述命令是什么(flag、约束、SafetySpec、Schema 元数据)
|
||||
- **执行面** — 钩子函数描述命令做什么(校验、派发、编排)
|
||||
|
||||
框架负责:flag 注册、有效值回退链、required/约束校验、SafetySpec 确认、toolArgs 装配、Agent Runtime Schema 投影。
|
||||
|
||||
## 核心类型
|
||||
|
||||
### corecmd.Spec
|
||||
|
||||
统一的类型化命令规格,是框架的核心数据结构:
|
||||
|
||||
```go
|
||||
type Spec struct {
|
||||
// 声明面
|
||||
Use string
|
||||
Short string
|
||||
Long string
|
||||
Example string
|
||||
Flags []FlagSpec
|
||||
Constraints []Constraint
|
||||
Safety cli.SafetySpec // 运行时与 Schema 的单一安全来源
|
||||
ConfirmFirst bool // 确认门先于参数校验
|
||||
ConstParams map[string]any
|
||||
Schema SchemaDecl // 完整 ToolSpec 载荷
|
||||
|
||||
// 执行面(恰好一个)
|
||||
Invoke func(c *Ctx, toolArgs map[string]any) error // 单步
|
||||
Orchestrate func(c *Ctx) error // 多步
|
||||
RunE func(cmd *cobra.Command, args []string) error // 逃生舱
|
||||
|
||||
// 钩子
|
||||
Validate func(cmd *cobra.Command, args []string) error
|
||||
PostMount func(cmd *cobra.Command)
|
||||
}
|
||||
```
|
||||
|
||||
### SafetySpec(单一安全来源)
|
||||
|
||||
`Spec.Safety` 直接使用现有 Agent Runtime Schema 的 `cli.SafetySpec`:
|
||||
|
||||
| 字段 | 职责 |
|
||||
|------|------|
|
||||
| `Effect` | 操作影响:read / write / destructive |
|
||||
| `Risk` | 风险等级:low / medium / high |
|
||||
| `Confirmation` | 是否需要用户确认:not_required / user_required |
|
||||
| `Idempotency` | 幂等性:idempotent / retryable / non_idempotent / unknown |
|
||||
|
||||
四个字段彼此独立。框架只读取 `Confirmation` 决定运行时确认,其余字段原样发布到 Schema,不从一个字段机械推导另一个。非空 SafetySpec 必须一次声明完整:
|
||||
|
||||
```go
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "write",
|
||||
Risk: "high",
|
||||
Confirmation: "user_required",
|
||||
Idempotency: "unknown",
|
||||
},
|
||||
```
|
||||
|
||||
完全空值保留历史只读默认 `read/low/not_required/idempotent`;不存在 Risk/Safety 枚举或覆盖优先级链。
|
||||
|
||||
### FlagSpec
|
||||
|
||||
声明一个 flag 的注册方式、有效值回退链、到 toolArgs 的绑定:
|
||||
|
||||
```go
|
||||
type FlagSpec struct {
|
||||
Name string // flag 名(kebab-case)
|
||||
Usage string // --help 文案
|
||||
Kind FlagKind // String / Int / Bool / StringSlice
|
||||
Default string // 注册默认值
|
||||
Required bool // 框架校验非空
|
||||
Aliases []string // 隐藏别名
|
||||
EnvVar string // 环境变量回退
|
||||
Bind string // toolArgs 键名(空则用 Name)
|
||||
Transform func(string) (any, error) // 值转换
|
||||
// ...更多字段见源码
|
||||
}
|
||||
```
|
||||
|
||||
### Constraint
|
||||
|
||||
跨 flag 关系约束:
|
||||
|
||||
```go
|
||||
type Constraint struct {
|
||||
Kind ConstraintKind // at_least_one / exactly_one / mutually_exclusive
|
||||
Flags []string
|
||||
}
|
||||
```
|
||||
|
||||
## 有效值回退链
|
||||
|
||||
flag 解析按以下顺序取值(先命中先生效):
|
||||
|
||||
```
|
||||
显式主 flag (Changed) → 隐藏别名 (Changed) → 环境变量 → 注册默认值
|
||||
│
|
||||
ArgDefault ←──┘ (兜底)
|
||||
```
|
||||
|
||||
- KindBool:仅 Changed 时生效,不参与回退链
|
||||
- KindStringSlice:仅主 flag / alias Changed 时生效,元素恒 TrimSpace
|
||||
- KindInt:非零才入 toolArgs(putInt 语义)
|
||||
|
||||
## 构建时流程
|
||||
|
||||
`corecmd.New(spec)` 执行以下构建时检查(失败则 panic):
|
||||
|
||||
1. **validateDispatchDecl** — 恰好一个执行体(Invoke/Orchestrate/RunE)
|
||||
2. **validateSafetySpec** — 非空 SafetySpec 的四个独立字段必须完整
|
||||
3. **validateSchemaDecl** — Schema 声明完整性(Description、AgentSummary、UseWhen、AvoidWhen、Examples、Interface)
|
||||
4. **RegisterFlags** — flag + alias 注册到 cobra
|
||||
5. **ValidateConstraintDecls** — 约束引用的 flag 必须存在
|
||||
6. **embedContractIntoSchema** — 投影到 dws.schema.* annotations
|
||||
7. **AnnotateConstraints** — 约束渲染到 --help
|
||||
8. **PostMount** — 调用方的挂载收尾钩子
|
||||
|
||||
## 运行时流程
|
||||
|
||||
生成的 `RunE` 按以下顺序执行:
|
||||
|
||||
```
|
||||
[ConfirmFirst? → ConfirmSafety] ← 可选:先确认后校验
|
||||
│
|
||||
▼
|
||||
ValidateRequired ← 有效值回退链校验
|
||||
│
|
||||
▼
|
||||
ValidateConstraints ← 互斥/至少一个/恰好一个
|
||||
│
|
||||
▼
|
||||
Validate hook ← 条件式业务校验(可选)
|
||||
│
|
||||
▼
|
||||
BuildArgs ← flag → toolArgs 装配
|
||||
│
|
||||
▼
|
||||
ConstParams 合并
|
||||
│
|
||||
▼
|
||||
[!ConfirmFirst? → ConfirmSafety] ← 默认顺序:校验后确认
|
||||
│
|
||||
▼
|
||||
Invoke(ctx, toolArgs) ← 单步派发
|
||||
或 Orchestrate(ctx) ← 多步编排
|
||||
```
|
||||
|
||||
## 消费方式
|
||||
|
||||
### LeafSpec(MCP 直连叶子命令)
|
||||
|
||||
```go
|
||||
func newDevAppCreateCommand(runner executor.Runner) *cobra.Command {
|
||||
return NewLeafCommand(LeafSpec{
|
||||
Use: "create",
|
||||
Short: "创建开放平台企业内部应用",
|
||||
Tool: devAppCreateTool,
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "write", Risk: "high",
|
||||
Confirmation: "user_required", Idempotency: "unknown",
|
||||
},
|
||||
ConfirmFirst: true,
|
||||
Flags: []LeafFlag{
|
||||
{Name: "name", Usage: "应用名称 (必填)", Bind: "name",
|
||||
Trim: true, Required: true, RequiredHint: "--name 为必填"},
|
||||
},
|
||||
Schema: LeafSchema{
|
||||
Description: "创建开放平台企业内部应用",
|
||||
DryRun: &LeafDryRunDecl{PreviewKind: "invocation"},
|
||||
Interface: &LeafInterfaceDecl{Mode: "composite", Availability: "available"},
|
||||
Selection: LeafSelectionDecl{
|
||||
AgentSummary: "创建钉钉开放平台应用",
|
||||
UseWhen: []string{"需要新建企业内部应用"},
|
||||
AvoidWhen: []string{"应用已存在时用 update"},
|
||||
Examples: []string{`dws dev app create --name "Bot" --dry-run`},
|
||||
},
|
||||
},
|
||||
Call: devAppCall(runner),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
`NewLeafCommand` 经 `FromLeafSpec()` 归一为 `corecmd.Spec`,再交 `corecmd.New()` 构建。这是**完全托管模式**:声明 + 执行都归 command。
|
||||
|
||||
### 声明元数据模式(既有命令补 Schema)
|
||||
|
||||
执行体必须冻结时,用同一套 `LeafSpec` 词汇只声明元数据,写在命令字面量旁:
|
||||
|
||||
```go
|
||||
baseListCmd := &cobra.Command{
|
||||
Use: "list", Short: "获取 AI 表格列表",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { /* 原执行体不动 */ },
|
||||
}
|
||||
DeclareLeafMetadata(baseListCmd, LeafSpec{
|
||||
Safety: aitableSafetyRead(),
|
||||
Schema: LeafSchema{
|
||||
Description: "列出最近访问的 AI 表格 Base。",
|
||||
Interface: aitableMCPInterface("list_bases"),
|
||||
Selection: LeafSelectionDecl{
|
||||
AgentSummary: "列出最近访问的 AI 表格 Base。",
|
||||
UseWhen: []string{"只需浏览最近打开过的 Base 时"},
|
||||
AvoidWhen: []string{"按名称查找优先 base search"},
|
||||
Examples: []string{"dws aitable base list"},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
`DeclareLeafMetadata` 调用 `corecmd.AttachSchema` 挂 Safety+Schema;不注册 flag、不接管参数投影。当 `Safety.Confirmation=user_required` 时,用**同一份** SafetySpec 包一层 `ConfirmSafety`,保证执行门禁与 Catalog 同源。迁移态入口;新命令仍应走 `NewLeafCommand`。
|
||||
|
||||
### Shortcut(智能快捷方式,已接入 live mount)
|
||||
|
||||
```go
|
||||
func mount(s Shortcut) *cobra.Command {
|
||||
return corecmd.New(FromShortcut(s))
|
||||
}
|
||||
|
||||
spec := FromShortcut(Shortcut{
|
||||
Service: "chat",
|
||||
Command: "+demo",
|
||||
Risk: RiskHighWrite,
|
||||
Flags: []Flag{...},
|
||||
Execute: func(rt *RuntimeContext) error { ... },
|
||||
})
|
||||
```
|
||||
|
||||
Shortcut 当前仍保留自身的 `Risk`,adapter 只在边界将它展开成完整
|
||||
`cli.SafetySpec`;command/Leaf 不再保留该枚举。Shortcut 的 Cobra
|
||||
type/default/usage provenance 保持不变,command 统一补充 Required、Enum 和关系约束投影。
|
||||
|
||||
## 文件结构
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `internal/corecmd/corecmd.go` | 核心类型 + `New` 构建器 + 运行时管线 |
|
||||
| `internal/corecmd/schema_decl.go` | SchemaDecl 载荷类型 + 声明完整性守卫 |
|
||||
| `internal/helpers/leaf.go` | LeafSpec 门面:`NewLeafCommand`(完全托管)+ `DeclareLeafMetadata`(声明元数据) |
|
||||
| `internal/shortcut/adapter.go` | FromShortcut 完整映射与 Risk 兼容边界 |
|
||||
| `internal/shortcut/runner.go` | RuntimeContext;live mount 委托 `corecmd.New(FromShortcut(s))` |
|
||||
|
||||
## Schema 投影
|
||||
|
||||
声明即 review:代码中的 Schema 声明经过 code review 后直接投影为:
|
||||
|
||||
- **Agent Runtime Schema** (dws.schema.* cobra annotations)
|
||||
- **Agent Metadata** (agent-metadata JSON, 消费方为 Agent 选择器)
|
||||
- **Catalog** (catalog.json, 命令注册表)
|
||||
- **Dry-run Capabilities** (声明自动索引为 reviewed 能力)
|
||||
|
||||
不再需要外部 hint 文件维护 selection/metadata/dry-run 信息。
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **声明 vs 执行分离** — Flags/Constraints/Safety/Schema 是声明;Invoke/Validate/PostMount 是执行
|
||||
2. **单一数据源** — 一份声明驱动 --help、Schema、catalog、runtime 校验
|
||||
3. **安全字段不互推** — Confirmation 单独驱动确认,Effect/Risk/Idempotency 原样发布
|
||||
4. **构建时拦截 > 运行时报错** — 声明不完整在命令注册时 panic,不等到用户触发
|
||||
5. **边界兼容** — Shortcut 暂由 adapter 转换,Leaf 直接声明 SafetySpec
|
||||
@@ -0,0 +1,236 @@
|
||||
# 命令框架对比:DWS command vs lark-cli vs GWS
|
||||
|
||||
本文档对比 DWS(钉钉工作区 CLI)、lark-cli(飞书 CLI)和 GWS(Google Workspace CLI / gcloud)三套命令框架的设计差异。
|
||||
|
||||
## 总览对比
|
||||
|
||||
| 维度 | DWS (command) | lark-cli | GWS (gcloud) |
|
||||
|------|---------------|----------|--------------|
|
||||
| 语言 | Go | Go | Python (gcloud) / Go (部分) |
|
||||
| CLI 框架 | cobra | cobra | argparse + calliope |
|
||||
| 调用底座 | MCP JSON-RPC | Lark REST SDK (`CallAPITyped`) | Google API Client |
|
||||
| 命令层次 | 2 层:LeafSpec + Shortcut | 3 层:Shortcuts + API Commands + Raw API | 2 层:surface commands + raw |
|
||||
| Schema 来源 | 代码声明投影 | 代码声明 + 运行时 introspection | API Discovery 文档自动生成 |
|
||||
| Agent 适配 | 内建 (dws.schema.*) | 内建 (--print-schema) | 外挂 (MCP adapter) |
|
||||
|
||||
## 架构对比
|
||||
|
||||
### DWS command
|
||||
|
||||
```
|
||||
corecmd.Spec (声明) → corecmd.New() → cobra.Command
|
||||
│
|
||||
├── cli.SafetySpec (运行时 + Schema 单一安全来源)
|
||||
├── FlagSpec[] (参数 + 回退链 + 绑定)
|
||||
├── Constraint[] (互斥/至少一个)
|
||||
├── SchemaDecl (Agent Selection/DryRun/Interface)
|
||||
│
|
||||
└── Invoke / Orchestrate / RunE (执行)
|
||||
```
|
||||
|
||||
**核心特点**:
|
||||
- 声明与执行严格分离
|
||||
- SafetySpec 四个独立字段直接对齐 Agent Runtime Schema
|
||||
- 有效值回退链:flag → alias → env → default
|
||||
- 框架统一校验、装配、确认、投影
|
||||
- Schema 从代码声明直接投影,无外部 hint 文件
|
||||
|
||||
### lark-cli
|
||||
|
||||
```
|
||||
Shortcut (声明) → runner.Mount() → cobra.Command
|
||||
│
|
||||
├── Risk string (确认行为)
|
||||
├── Scopes / ConditionalScopes (OAuth 权限)
|
||||
├── Flag[] (参数 + Enum + Input sources)
|
||||
├── AuthTypes (user/bot)
|
||||
│
|
||||
├── DryRun hook → DryRunAPI
|
||||
├── Validate hook
|
||||
└── Execute hook → RuntimeContext → CallAPITyped
|
||||
```
|
||||
|
||||
**核心特点**:
|
||||
- Execute 内直接调 REST API (`CallAPITyped`)
|
||||
- DryRun 是独立 hook(返回结构化 API 计划)
|
||||
- 内建 OAuth scope 声明与预检
|
||||
- `--print-schema --flag-name` 运行时 introspection
|
||||
- 无 Schema 投影层,Agent 通过 introspection 动态发现
|
||||
|
||||
### GWS (gcloud 风格)
|
||||
|
||||
```
|
||||
API Discovery → 代码生成 → surface command
|
||||
│
|
||||
├── arguments (从 JSON Schema 自动生成)
|
||||
├── request/response 映射
|
||||
└── 自定义 action hook (少量)
|
||||
```
|
||||
|
||||
**核心特点**:
|
||||
- Schema-first:从 API Discovery 文档自动生成命令
|
||||
- 参数直接映射 API 字段(flat schema)
|
||||
- 人工 surface command 是 thin wrapper
|
||||
- Agent 适配通过 MCP 外部 adapter
|
||||
|
||||
## 核心设计差异
|
||||
|
||||
### 1. 声明粒度
|
||||
|
||||
| 能力 | DWS command | lark-cli | GWS |
|
||||
|------|-------------|----------|-----|
|
||||
| 参数别名 + 环境变量回退 | ✅ FlagSpec.Aliases + EnvVar | ❌ 无 | ❌ 无 |
|
||||
| 声明式约束 (互斥/至少一个) | ✅ Constraint[] | ❌ 只有 Validate hook | ✅ argparse group |
|
||||
| 安全契约 | ✅ SafetySpec(effect/risk/confirmation/idempotency) | Risk | 无 |
|
||||
| Schema 投影 (Agent metadata) | ✅ SchemaDecl 内建 | ⚠️ 运行时 introspection | ❌ 外挂 |
|
||||
| 参数绑定 (flag name → API key) | ✅ FlagSpec.Bind | ❌ 手写 | ✅ 自动映射 |
|
||||
| ConstParams (固定载荷) | ✅ | ❌ 手写在 Execute | ✅ 隐式 |
|
||||
| 确认门顺序可配 (ConfirmFirst) | ✅ | ❌ 固定顺序 | ❌ 无确认机制 |
|
||||
|
||||
### 2. 执行模型
|
||||
|
||||
| 维度 | DWS command | lark-cli | GWS |
|
||||
|------|-------------|----------|-----|
|
||||
| 参数装配 | 框架自动 (BuildArgs) | 手写 (`runtime.Str()/Bool()`) | 自动映射 |
|
||||
| 派发方式 | Invoke(ctx, toolArgs) | Execute(ctx, runtime) | 自动调用 |
|
||||
| 多步编排 | Orchestrate(ctx) | Execute 内链式 CallAPITyped | 不支持 |
|
||||
| DryRun | 框架统一 (--dry-run flag) | 独立 DryRun hook 返回 API 计划 | 部分命令支持 |
|
||||
| 错误分类 | apperrors 类型化 | errs.Problem 类型化 | HTTP status 映射 |
|
||||
|
||||
### 3. Agent 适配
|
||||
|
||||
| 维度 | DWS command | lark-cli | GWS |
|
||||
|------|-------------|----------|-----|
|
||||
| 工具发现 | `dws schema --all` (静态 catalog) | `--print-schema` (运行时) | API Discovery |
|
||||
| 选择指引 | SelectionDecl (UseWhen/AvoidWhen) | Description + Tips | 无 |
|
||||
| 安全声明 | cli.SafetySpec 直接声明 | Risk string | 无 |
|
||||
| dry-run 能力声明 | DryRunDecl (reviewed) | DryRun hook 存在性 | 无 |
|
||||
| 接口模式 | InterfaceDecl (local/pinned/composite) | 隐式 (全部 REST) | 隐式 (全部 REST) |
|
||||
|
||||
### 4. Schema 生命周期
|
||||
|
||||
```
|
||||
DWS: 代码声明 → code review → cobra annotation → catalog/metadata JSON
|
||||
(单一数据源,构建时验证完整性)
|
||||
|
||||
lark-cli: 代码声明 → 运行时 introspection → Agent 动态发现
|
||||
(无离线 catalog,Agent 必须执行命令才能发现)
|
||||
|
||||
GWS: API Discovery JSON → 代码生成 → surface command
|
||||
(Schema-first,但命令行体验受限于 API 形状)
|
||||
```
|
||||
|
||||
## 设计哲学对比
|
||||
|
||||
### DWS command 的选择
|
||||
|
||||
| 选择 | 理由 | 对比 |
|
||||
|------|------|------|
|
||||
| 框架装配参数 | 消除 N 个命令各写一份 toolArgs 装配 | lark-cli 每个 Execute 手动取 flag 值 |
|
||||
| SafetySpec 单一来源 | confirmation 驱动运行时,其余字段原样发布且互不推导 | lark-cli 只有 Risk 一个维度 |
|
||||
| 声明式约束 | 构建时校验合法性 + 投影到 Schema + 渲染帮助 | lark-cli 约束隐藏在 Validate 逻辑里 |
|
||||
| Schema 构建时投影 | 离线 catalog 支持 Agent 批量发现 | lark-cli 需要逐个命令 introspection |
|
||||
| 有效值回退链 | flag → alias → env 统一语义 | lark-cli 别名是独立 Flag 手动关联 |
|
||||
| ConfirmFirst | 精确建模遗留语义 | lark-cli 确认始终在 Execute 内 |
|
||||
|
||||
### lark-cli 的选择
|
||||
|
||||
| 选择 | 理由 | 对比 |
|
||||
|------|------|------|
|
||||
| 直连 REST API | 精确控制请求/响应,可处理分页/重试 | DWS 通过 MCP 间接调用 |
|
||||
| DryRun 返回 API 计划 | Agent 可预览将要发出的真实 HTTP 请求 | DWS dry-run 只展示参数 |
|
||||
| OAuth scope 声明 | 框架预检权限,失败提前 | DWS 依赖 MCP 层鉴权 |
|
||||
| `--print-schema` introspection | 运行时发现,无需维护离线 catalog | DWS 需要 re-generate |
|
||||
| Input sources (file/@path/stdin) | 丰富的输入方式声明 | DWS 无此抽象 |
|
||||
| PrintFlagSchema | 单 flag 级别的 JSON Schema 暴露 | DWS 只在 catalog 级别 |
|
||||
|
||||
### GWS (gcloud) 的选择
|
||||
|
||||
| 选择 | 理由 | 对比 |
|
||||
|------|------|------|
|
||||
| API Discovery 驱动 | 一份 Schema 生成所有:SDK/CLI/文档 | DWS/lark 手写 |
|
||||
| Flat parameter 映射 | API 字段 = CLI flag,零转换 | DWS 需要 Bind 映射 |
|
||||
| 无 shortcut 层 | API 粒度即用户粒度 | DWS/lark 有精选层 |
|
||||
|
||||
## 代码量对比
|
||||
|
||||
| 框架 | 核心框架代码 | 单命令声明开销 | 备注 |
|
||||
|------|-------------|---------------|------|
|
||||
| DWS command | ~1400 行 (command.go + schema_decl.go) | ~20-30 行 (纯声明) | 框架重、单命令轻 |
|
||||
| lark-cli | ~800 行 (runner.go + types.go + common.go) | ~50-150 行 (声明 + Execute 逻辑) | 框架轻、单命令重 |
|
||||
| GWS gcloud | ~5000+ 行 (calliope 框架) | ~10 行 (多数自动生成) | 框架最重、单命令最轻 |
|
||||
|
||||
## DWS 命令声明示例 vs lark-cli
|
||||
|
||||
### DWS (command / LeafSpec)
|
||||
|
||||
```go
|
||||
NewLeafCommand(LeafSpec{
|
||||
Use: "create",
|
||||
Short: "创建应用",
|
||||
Tool: "create_dev_app",
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "write", Risk: "high",
|
||||
Confirmation: "user_required", Idempotency: "unknown",
|
||||
},
|
||||
ConfirmFirst: true,
|
||||
Flags: []LeafFlag{
|
||||
{Name: "name", Usage: "应用名称", Bind: "name",
|
||||
Trim: true, Required: true, RequiredHint: "--name 为必填"},
|
||||
},
|
||||
Schema: LeafSchema{
|
||||
Description: "创建开放平台企业内部应用",
|
||||
DryRun: &LeafDryRunDecl{PreviewKind: "invocation"},
|
||||
Interface: &LeafInterfaceDecl{Mode: "composite", Availability: "available"},
|
||||
Selection: LeafSelectionDecl{
|
||||
AgentSummary: "创建钉钉开放平台应用",
|
||||
UseWhen: []string{"需要新建企业内部应用"},
|
||||
AvoidWhen: []string{"应用已存在时用 update"},
|
||||
Examples: []string{`dws dev app create --name "Bot" --dry-run`},
|
||||
},
|
||||
},
|
||||
Call: devAppCall(runner),
|
||||
})
|
||||
```
|
||||
|
||||
### lark-cli (Shortcut)
|
||||
|
||||
```go
|
||||
var CalendarCreate = common.Shortcut{
|
||||
Service: "calendar",
|
||||
Command: "+create",
|
||||
Description: "Create a new calendar event",
|
||||
Risk: "write",
|
||||
Scopes: []string{"calendar:calendar"},
|
||||
Flags: []common.Flag{
|
||||
{Name: "summary", Desc: "Event title", Required: true},
|
||||
{Name: "start", Desc: "Start time (RFC3339)", Required: true},
|
||||
{Name: "end", Desc: "End time (RFC3339)", Required: true},
|
||||
{Name: "attendees", Type: "string_slice", Desc: "Attendee emails"},
|
||||
},
|
||||
DryRun: func(ctx context.Context, rt *common.RuntimeContext) *common.DryRunAPI {
|
||||
return &common.DryRunAPI{
|
||||
Method: "POST",
|
||||
Path: "/open-apis/calendar/v4/calendars/{id}/events",
|
||||
Body: buildEventBody(rt),
|
||||
}
|
||||
},
|
||||
Execute: func(ctx context.Context, rt *common.RuntimeContext) error {
|
||||
body := buildEventBody(rt)
|
||||
data, err := rt.CallAPITyped("POST",
|
||||
"/open-apis/calendar/v4/calendars/{id}/events", nil, body)
|
||||
if err != nil { return err }
|
||||
return rt.Output(data)
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 适用场景总结
|
||||
|
||||
| 场景 | 最适合 | 原因 |
|
||||
|------|--------|------|
|
||||
| MCP 后端 + Agent Schema 投影 | **DWS command** | 内建 Schema 声明、SafetySpec 契约、离线 catalog |
|
||||
| REST API 直连 + OAuth scope 管理 | **lark-cli** | CallAPITyped + scope 预检 + DryRun API 计划 |
|
||||
| API-first 大规模 surface 生成 | **GWS gcloud** | Discovery 驱动,一份 Schema 生成一切 |
|
||||
| 多步编排 (跨服务链式调用) | **lark-cli** / DWS Orchestrate | lark 的 CallAPITyped 链式 + DWS 的 Orchestrate |
|
||||
| 遗留系统迁移 (保持行为等价) | **DWS command** | ConfirmFirst + 回退链 + catalog 漂移门禁 |
|
||||
@@ -0,0 +1,246 @@
|
||||
# flag / help / schema 同源
|
||||
|
||||
- **状态**:已决策(路径 A + Contract 嵌入 Schema)
|
||||
- **相关**:[`rfc-command-framework-convergence.md`](rfc-command-framework-convergence.md)、[`schema-dynamic-endpoint-design.md`](schema-dynamic-endpoint-design.md)
|
||||
- **实现门面**:`LeafSpec` → `corecmd.Spec` → `corecmd.New`
|
||||
|
||||
## 1. 决策
|
||||
|
||||
采用 **路径 A:Contract / LeafSpec 为 CLI 表面权威**,并且 **Contract 必须嵌入进 Schema**。
|
||||
|
||||
「同源」的含义是:同一份 Contract(今日经 LeafSpec / `corecmd.Spec` 表达)同时决定——且门禁能证明——
|
||||
|
||||
1. cobra 实际注册的 flags / required / defaults;
|
||||
2. `--help` 的 Flags 与「参数约束」段;
|
||||
3. **嵌入后的** `dws schema` / Catalog(`schema_catalog` / agent metadata 生成物)中的 parameters、关系约束和 SafetySpec。
|
||||
|
||||
嵌入机制(已落地):
|
||||
|
||||
```text
|
||||
corecmd.Spec
|
||||
→ RegisterFlags + embedContractIntoSchema
|
||||
→ cobra annotations:
|
||||
dws.schema.contract=command
|
||||
dws.schema.property / type / required (per flag)
|
||||
dws.schema.constraints
|
||||
→ RegisterRuntimeContractFinal(SafetySpec + SchemaDecl)
|
||||
→ Schema 组装透传 Contract Final
|
||||
→ go:embed schema_catalog / schema_agent_metadata
|
||||
```
|
||||
|
||||
command/Leaf 不再写 `dws.schema.risk`;SafetySpec 走类型化 Final 载荷,不使用字符串枚举注解。
|
||||
|
||||
### 1.1 硬规则:声明 = 最终数据源(Schema 透传)
|
||||
|
||||
受管命令进入 Schema 的叶子数据由 **Contract 声明**定义最终值;框架(`corecmd.New`)做**类型转换**并注册,Schema 组装**透传**,不得:
|
||||
|
||||
- 把声明序列化成 JSON 注解再解析;
|
||||
- 在声明体系里再挂「评审字段」并行权威;
|
||||
- 用 hints/registry 盖写已声明字段。
|
||||
|
||||
迁移期未迁完的叶子可暂走旧组装路径;**新声明面不含 review_reason / reviewed 字段**。写命令未设 `Safety` 时,过渡期仍可用 `runtime_gate` annotate(`HOM-S2`)。
|
||||
|
||||
**不采用**路径 B(以 `schema_mcp_metadata` 生成全部 CLI flag/help/schema)作为主权威。钉钉 MCP meta 不是飞书 OAPI:粒度与 CLI 特有语义(二选一、OmitEmpty、ConstParams、write guard)无法从裸 meta 推出;强行生成会违反「Schema 描述 CLI,不制造 CLI」。
|
||||
|
||||
路径 B 仅允许作为 **可选的 1:1 MCP 透传叶子通道**(见 §5),不得覆盖 LeafSpec / Shortcut 主路径。
|
||||
|
||||
对外叙事:与飞书 **分层单权威** 同构——API/透传叶可用平台事实,产品 CLI / Shortcut 用手写契约 + 执行体——而不是「全家只有平台 meta」。
|
||||
|
||||
### 1.2 声明(declare):写什么、写在哪、投影到哪
|
||||
|
||||
**定义**:声明 = 在 Contract 结构体的**数据字段**上写出事实;`corecmd.New` 据此注册 cobra、渲染 help、写入 `dws.schema.*`。钩子闭包(`Validate` / `Call` / `PostMount` / `RunE`)里的逻辑**不算**声明——即使行为正确,也不能单靠钩子让 Schema/help「猜出」该事实。
|
||||
|
||||
命令框架边界与今日/目标对应见 RFC [`rfc-command-framework-convergence.md`](rfc-command-framework-convergence.md) **§5.0**(框架上的「声明」定义);本节给字段级表与示例。
|
||||
|
||||
**唯一写入面**(三选一,语义相同):
|
||||
|
||||
| 入口 | 类型 | 归一 |
|
||||
|---|---|---|
|
||||
| Leaf 命令 | `helpers.LeafSpec` | `FromLeafSpec` → `corecmd.Spec` |
|
||||
| Shortcut | Shortcut 声明(经 `FromShortcut`) | → `corecmd.Spec` |
|
||||
| 直接基座 | `corecmd.Spec` | `corecmd.New` |
|
||||
|
||||
今日产品 CLI 叶子以 **`LeafSpec` 为声明门面**;字段与 `corecmd.Spec` 契约面一一对应。
|
||||
|
||||
#### 1.2.1 契约字段(算声明)
|
||||
|
||||
| 字段 | 声明什么 | 运行时 | 嵌入 Schema / help |
|
||||
|---|---|---|---|
|
||||
| `Flags[]`(`FlagSpec` / `LeafFlag`) | 用户可见参数面:名、类型、默认、必填、usage | 注册 cobra flag;装配 toolArgs | `dws.schema.property` / `type` / `required`;`--help` Flags |
|
||||
| `Constraints[]` | 跨 flag 关系:`at_least_one` / `exactly_one` / `mutually_exclusive`;`custom` 记录钩子校验 | 通用关系由 `ValidateConstraints` 执行;`custom` 由 `Validate` 执行 | `dws.schema.constraints`;`--help`「参数约束」 |
|
||||
| `Safety`(`cli.SafetySpec`) | effect/risk/confirmation/idempotency 四个独立事实 | `confirmation=user_required` 时 `ConfirmSafety`;`--yes` / `--dry-run` 跳过 | 同一个 SafetySpec 原样进入 Contract Final(`HOM-S1`) |
|
||||
| `ConstParams` | 固定载荷(不上 flag 表) | 并入 toolArgs;不满足 Required | **不**投影为用户 parameter |
|
||||
| `Use` / `Short` / `Long` / `Example` | 命令身份文案与示例 | cobra 自身 | help;identity 仍以 registry 为准 |
|
||||
|
||||
`FlagSpec` 子字段(声明细节):
|
||||
|
||||
| 子字段 | 作用 | 是否进 Schema parameters |
|
||||
|---|---|---|
|
||||
| `Name`, `Usage`, `Kind`, `Default` | 注册名/说明/类型/DefValue | 是(name/type;default 与 cobra 对齐) |
|
||||
| `Required` / `MarkRequired` | 非空校验 / cobra 硬必填 | 是(`required`) |
|
||||
| `RequiredHint`, `Aliases`, `EnvVar` | 校验提示、隐藏别名、环境回退 | 否(执行细节;别名不上主 parameter 表) |
|
||||
| `ArgDefault`, `Bind`, `OmitEmpty`, `Trim`, `Transform` | toolArgs 装配语义 | 否(载荷细节;`Bind` 可进 property 映射,但不另造 flag) |
|
||||
|
||||
#### 1.2.2 编排 / 执行字段(不算声明)
|
||||
|
||||
| 字段 | 角色 | 禁止用来「冒充」的声明 |
|
||||
|---|---|---|
|
||||
| `Validate` | 条件式/领域校验钩子 | 不得在此 `Flags().String(...)` 注册业务 flag;不得只靠钩子表达「必填/互斥」而不写 `Flags`/`Constraints` |
|
||||
| `Call` / `Invoke` / `Orchestrate` | 执行体 | 不得 `params[k]=…` 装配业务参数(应在 `Flags`/`ConstParams`) |
|
||||
| `PostMount` | 挂载后收尾(annotate、领域工具注入) | 不得注册业务 flag;分页等横切由领域工具注入并可走 annotate |
|
||||
| `RunE` | 逃生舱(整段手写) | 表面事实仍须 Flags 声明;框架仍按 Safety 执行确认 |
|
||||
| `Server` / `Tool` | MCP 路由 | 不构成 CLI parameter 声明 |
|
||||
|
||||
#### 1.2.3 最小声明示例
|
||||
|
||||
```go
|
||||
// 读:Safety 四字段显式对齐 Schema
|
||||
NewLeafCommand(LeafSpec{
|
||||
Use: "get", Short: "…", Tool: "…",
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Flags: []LeafFlag{
|
||||
{Name: "unified-app-id", Usage: "…", Bind: "unifiedAppId", Trim: true, Required: true},
|
||||
},
|
||||
Call: devAppCall(runner), PostMount: devAppMeta(tool),
|
||||
})
|
||||
|
||||
// 写:同一个 SafetySpec 同时驱动确认与 Schema
|
||||
NewLeafCommand(LeafSpec{
|
||||
Use: "publish", Short: "…", Tool: "…",
|
||||
Flags: []LeafFlag{ /* … */ },
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "write", Risk: "medium",
|
||||
Confirmation: "user_required", Idempotency: "unknown",
|
||||
},
|
||||
Call: devAppCall(runner), PostMount: devAppMeta(tool),
|
||||
})
|
||||
|
||||
// 迁移期旧写命令:确认走 annotate,见 §1.3 —— 不是新声明面
|
||||
NewLeafCommand(LeafSpec{
|
||||
Use: "create", /* Flags… */,
|
||||
Validate: func(cmd *cobra.Command, _ []string) error {
|
||||
return devAppRequireWriteGuard(cmd, "create") // 执行守卫,不是 Contract 声明
|
||||
},
|
||||
Call: devAppCall(runner),
|
||||
PostMount: devAppMetaWrite(tool), // 人工标注 runtime_gate
|
||||
})
|
||||
```
|
||||
|
||||
**空 `Safety` 的含义**:command 为兼容旧只读叶保留 `read/low/not_required/idempotent` 默认。因此「会改状态却留空 Safety」**不是**合法声明;新 Leaf 必须写完整 SafetySpec,未迁移旧路径则按 §1.3 标注 gate。
|
||||
|
||||
### 1.3 人工标注(annotate):声明的补充通道
|
||||
|
||||
当事实无法或不愿放进 Contract 字段时,必须**显式**落注解 / 评审源,禁止组装期推断:
|
||||
|
||||
| 标注手段 | 典型值 | 何时用 |
|
||||
|---|---|---|
|
||||
| `cli.AnnotateRuntimeGate` / `devAppMetaWrite` | `dws.schema.runtime_gate=devAppRequireWriteGuard` | 尚未迁移到 SafetySpec 的旧写命令(`HOM-S2`) |
|
||||
| `cli.AnnotateRuntimeRisk` | `dws.schema.risk=…` | Shortcut 暂存的旧兼容路径;command/Leaf 禁止新增 |
|
||||
| `cli.AnnotateRuntimeFlag` / Constraints | 与 embed 同形 | 手写 cobra 叶补齐表面(长期应迁入 Contract) |
|
||||
| reviewed `schema_hints/metadata` Safety | effect/risk/confirmation | 迁移期遗留;受管命令收敛后让位于 Safety/gate |
|
||||
|
||||
标注与声明冲突时:**Contract 声明胜**(路径 A)。标注不得发明未注册的 CLI flag。
|
||||
|
||||
### 1.4 Schema 全覆盖(`ToolSpec` 无空洞)
|
||||
|
||||
`dws schema` 叶子模型是 `cli.ToolSpec`。命令框架必须为**每一个字段组**指定权威;完整矩阵在 RFC **§5.0.4**,摘要:
|
||||
|
||||
| ToolSpec 组 | 权威类 | 框架声明字段 / 其它源 |
|
||||
|---|---|---|
|
||||
| Identity | 评审源 | `schema_command_registry` |
|
||||
| Parameters.`name/type/required/default/property` | **声明**(或同形 annotate) | `Flags` / `Bind` |
|
||||
| Parameters.`description` | 声明 usage + 可选 hints overlay | `Usage`;hints 不得改 type/required/default |
|
||||
| Parameters.`interface_*` | 评审源 | MCP meta / bindings;**不造 flag** |
|
||||
| Constraints | **声明** | `Constraints` |
|
||||
| Positionals | **声明** 或显式 annotate | 目标 `Args`;禁止推断 |
|
||||
| Safety.`effect/risk/confirmation/idempotency` | **声明**完整 `Safety`,或迁移期 `runtime_gate` / reviewed Safety | 四字段独立;不得互相推导 |
|
||||
| DryRun | 评审源 | dry-run capabilities registry |
|
||||
| Interface | 评审源 | MCP + agent metadata |
|
||||
| Selection | 评审源 | `schema_hints/selection` |
|
||||
| FieldProvenance / Extensions | 组装派生或评审扩展 | 组装器;与 delivered value 一致 |
|
||||
| (非 Schema parameter)ConstParams | **声明** | 载荷;不上 parameters 表 |
|
||||
|
||||
验收:新增 Schema 字段必须同步改 RFC §5.0.4 + 本表;受管写命令 Safety 不得无主。
|
||||
|
||||
## 2. 字段归属(每一类恰好一个写入者)
|
||||
|
||||
| 字段类 | 权威 | 投影到 |
|
||||
|---|---|---|
|
||||
| flags / defaults / required / enum / 关系约束 / 运行时 Risk | Contract(LeafSpec / `corecmd.Spec` 门面) | cobra、`--help`、Schema `parameters` / constraints / confirmation |
|
||||
| ConstParams、Bind、OmitEmpty、Transform | 同上(载荷声明,不上 flag 表) | toolArgs;Schema 不把 ConstParams 伪装成用户 flag |
|
||||
| canonical path / aliases / navigation / exposure | `schema_command_registry`(+ reviewed manual additions) | Schema identity |
|
||||
| use_when / avoid_when / examples / agent_summary 文案 | `schema_hints/selection` | Schema selection |
|
||||
| RPC tool 形状、`interface_ref`、interface 描述 | `schema_mcp_metadata` + `schema_parameter_bindings` | Schema `interface_*` 字段;**不得创建 flag** |
|
||||
| 参数描述 overlay(可选) | `schema_hints/metadata` 仅补充 usage 文案 | 与 Contract/cobra 冲突时 **Contract/cobra 胜** |
|
||||
| 遗留 Safety 文案(迁移期) | 今日仍可读 `schema_hints/metadata` safety | 受管命令收敛后以 Contract.Safety / runtime_gate 为准(见 §4) |
|
||||
| dry-run 正能力 | reviewed dry-run registry | Schema `dry_run` |
|
||||
| positionals | Contract Args / 显式 annotate | Schema `positionals` |
|
||||
| FieldProvenance | 组装派生 | Schema provenance(与值一致) |
|
||||
|
||||
Identity 与 selection **刻意不**由 Contract 取代(RFC 决策 8 / schema 设计硬规则)。**完整无空洞表见 RFC §5.0.4。**
|
||||
|
||||
## 3. 当前缺口与目标闭环
|
||||
|
||||
已具备:
|
||||
|
||||
- Flags / ConstParams / Constraints → 注册、校验与 `ConstraintHelp`;SafetySpec → 运行时 `ConfirmSafety`(command);
|
||||
- Call / Execute 作为执行体;业务参数不得在 Call 内装配(helpers 门禁);
|
||||
- **Contract → Schema 嵌入**:参数/约束写原生 annotation,SafetySpec 与 SchemaDecl 注册为类型化 Contract Final 并由 Schema 组装透传。
|
||||
|
||||
仍缺:
|
||||
|
||||
1. `HOM-P*` / `HOM-D1` 门禁:禁止 hints 静默改写 type/required/default;help ≡ schema parameters;
|
||||
2. 重新 `make generate-schema` 后把嵌入结果固化进签入 catalog(注意本机 OOM);identity/selection 仍走评审源。
|
||||
|
||||
已落地(写命令确认语义,`HOM-S2` 起点):
|
||||
|
||||
- `AnnotateRuntimeGate` / `dws.schema.runtime_gate`;Leaf `PostMount: devAppMetaWrite`;手写 delete/robot 等同路径显式标注;
|
||||
- Schema 组装在无 Contract Safety 但有 gate 时 overlay `confirmation=user_required`(`applyContractGateToSafety`);
|
||||
- AST/mount 测试:新 Leaf 须声明完整 SafetySpec;尚未迁移的旧路径须有 runtime_gate。
|
||||
|
||||
## 4. Schema 投影与 Safety 门禁规划
|
||||
|
||||
以下门禁在落地时应作为独立 policy / 测试交付(门禁 ID 稳定,便于 CI 认领)。
|
||||
|
||||
| Gate ID | 断言 | 范围 |
|
||||
|---|---|---|
|
||||
| `HOM-P1` | 受管 leaf 的 schema `parameters[].name` 集合 ≡ cobra 本地 flag 名集合(排除全局 persistent) | LeafSpec / 未来 Contract 编译命令 |
|
||||
| `HOM-P2` | schema parameter `type` / `required` / `default` 与 cobra DefValue / MarkFlagRequired / FlagSpec 一致;hints overlay **不得**改写这三项,只可补 description | 同上 |
|
||||
| `HOM-P3` | schema 关系约束(require_one_of / mutually_exclusive)≡ Contract/Leaf `Constraints` 投影(与 `AnnotateConstraints` 同构) | 声明了 Constraints 的命令 |
|
||||
| `HOM-S1` | 若 Contract/Leaf `Risk` ∈ {write, high-risk-write},则 schema `confirmation=user_required`,且 help Safety 行与之同语义 | 使用 Risk 确认的受管命令 |
|
||||
| `HOM-S2` | 若命令走显式 write guard(如 `devAppRequireWriteGuard`)而非 Risk,则必须人工标注 `dws.schema.runtime_gate`(或等价 reviewed Safety);Schema 不得呈 `confirmation=not_required`;符合 §1.1 declare OR annotate | 今日 devapp 写命令 |
|
||||
| `HOM-S3` | `Risk=read`(或空→read)不得投影为 `user_required`,除非有 reviewed exclusion reason | 受管读命令 |
|
||||
| `HOM-I1` | `interface_ref` 存在时,bindings 覆盖的 CLI flag ⊆ Contract flags;MCP meta **不**引入额外 CLI flag | 有 MCP 绑定的命令 |
|
||||
| `HOM-D1` | `dws <path> --help` Flags 段与 schema leaf parameters 零未解释增量 | 抽样 + 受管全集逐步扩大 |
|
||||
|
||||
落地顺序建议:
|
||||
|
||||
1. 先对 LeafSpec 命令实现 `HOM-P1`/`HOM-P2`/`HOM-P3`(反射 cobra 即可起步,因 flag 已由 Contract 注册);
|
||||
2. 再收 `HOM-S1`–`S3`(需产品确认 devapp 是否升格 Risk,或保持 guard + 诚实 provenance);
|
||||
3. `HOM-I1`/`HOM-D1` 接入 `check-schema-*` / PR 门禁。
|
||||
|
||||
## 5. 路径 B 子通道:1:1 MCP 透传叶(可选,非主路径)
|
||||
|
||||
仅当同时满足以下条件时,才允许「从 MCP meta 生成 flag/help/schema 参数」:
|
||||
|
||||
1. CLI path ↔ 单一 MCP tool **严格 1:1**,无多步、无按名解析、无本地 effect;
|
||||
2. 无不在 MCP input schema 中的 CLI 特有 flag(含 guard 专用语义 flag 除外的全局 `--yes`/`--dry-run`);
|
||||
3. 无 ConstParams / Transform / 跨 flag 约束 / Call 内业务逻辑;
|
||||
4. Risk/confirmation 在 meta 或并列 reviewed Safety 中有显式来源,不靠生成器猜测;
|
||||
5. 在 registry 中标记 `surface_kind=mcp_passthrough`(名称可调整),且 **不得**与 LeafSpec/Shortcut 手写定义双注册同一 `cli_path`;
|
||||
6. 文档与门禁写明:该通道是子集优化,失败时回退/禁止扩张到产品 CLI。
|
||||
|
||||
显式排除(永远走路径 A / Shortcut):
|
||||
|
||||
- 全部 `LeafSpec` 命令(含 `dws dev app …`);
|
||||
- 全部 `+shortcut` 与 smart 编排;
|
||||
- 任何需要 `devAppRequireWriteGuard`、cursor 工具注入、或响应投影的命令。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不把 selection 文案或 canonical identity 塞进 Contract。
|
||||
- 不要求删除 LeafSpec 门面。
|
||||
- 不把「生成 catalog 字节一致」当作运行时同源的充分条件(仍需 `HOM-*` 与差分门禁)。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,12 +6,14 @@ DWS Schema 是当前二进制公开 CLI 的版本化 Agent 执行契约。它描
|
||||
|
||||
设计遵循三条硬规则:
|
||||
|
||||
1. **Schema 描述 CLI,不制造 CLI。** `CommandRegistry`、manual hint、metadata 和 Catalog 都不能凭空创建 Cobra 命令或 flag;registry 中的每个路径都必须精确绑定真实 runnable Cobra leaf。
|
||||
1. **Schema 描述 CLI,不制造 CLI。** `CommandRegistry`、manual hint、metadata 和 Catalog 都不能凭空创建 Cobra 命令或 flag;registry 中的每个路径都必须精确绑定真实 runnable Cobra leaf。**`schema_mcp_metadata` 同样不得生成 CLI flag**——它只提供 interface 事实(见 §4.1 同源决策)。
|
||||
2. **所有来源只解析一次。** 来源经过统一 resolver 进入 typed `SchemaRegistry`,所有查询、导出和门禁都消费同一个 `SchemaRegistry/SchemaIndex`。
|
||||
3. **Registry-first,Catalog 只出不进。** reviewed `CommandRegistry` 是稳定 command identity/navigation 的唯一事实源;`schema_catalog/`(`catalog.json` + 每产品 `tools/<product>.json`)和其他生成 JSON 只是下游发布物,不能成为命令、metadata 或下一轮 Catalog 的来源。运行时 production loader 解码 embedded snapshot 只是交付边界,不是 source resolution。
|
||||
|
||||
Schema 不调用 MCP `tools/list`,不访问网络,也不读取用户本地 discovery cache。
|
||||
|
||||
**flag / help / schema 参数面同源**(已决策):Contract / LeafSpec 为 CLI 表面权威;分层字段归属与门禁 ID 见 [`flag-help-schema-homology.md`](flag-help-schema-homology.md)。
|
||||
|
||||
## 2. 单向数据流
|
||||
|
||||
```text
|
||||
@@ -95,8 +97,10 @@ DWS 与 Lark 保持**架构同构**,而不是强行复制字段:
|
||||
| typed command/metadata registry | `EffectiveCommandRegistry`、`BoundCommandRegistry` 与最终 `SchemaRegistry` |
|
||||
| navigation catalog/index | 从同一 `ToolSpec` 派生的 `SchemaIndex` |
|
||||
| schema renderer/envelope | overview、product/group、leaf、`--all` projections |
|
||||
| API Commands ← 平台 OAPI meta | **不**作为 DWS 主路径;可选 1:1 MCP 透传子集见同源文档 §5 |
|
||||
| Shortcuts ← 手写声明 + Execute | LeafSpec / Shortcut + Contract 表面(路径 A) |
|
||||
|
||||
共同点是:强类型 registry 持有已审核、已绑定、已解析的事实,index 只负责确定性导航,renderer 只投影,不重新读取来源或做 precedence。DWS 的 base Registry 与 reviewed manual command additions 在绑定前合并为唯一的 `EffectiveCommandRegistry`,因此不存在 “native-first”、“legacy registry fallback” 或 Catalog fallback。
|
||||
共同点是:强类型 registry 持有已审核、已绑定、已解析的事实,index 只负责确定性导航,renderer 只投影,不重新读取来源或做 precedence。DWS 的 base Registry 与 reviewed manual command additions 在绑定前合并为唯一的 `EffectiveCommandRegistry`,因此不存在 “native-first”、“legacy registry fallback” 或 Catalog fallback。飞书也是**分层单权威**(API 用平台 meta,Shortcut 用手写契约),不是全家只有 meta——DWS 对齐的是这一分层,而不是「用 MCP meta 生成全部 CLI」。
|
||||
|
||||
DWS 内部 resolved model 为:
|
||||
|
||||
@@ -121,20 +125,33 @@ DWS 当前对外仍保留兼容 wire:leaf 使用 flat `parameters`,安全和
|
||||
|
||||
| 来源 | 负责内容 | 明确不负责 |
|
||||
|---|---|---|
|
||||
| Contract / LeafSpec / `corecmd.Spec` | **CLI 表面权威**:flags、defaults、required、enum、关系约束、运行时 Risk;编译为 cobra 与 help | canonical identity、selection 文案、虚构 RPC |
|
||||
| `schema_command_registry.json` | reviewed `CommandRegistry`:稳定 canonical identity、primary CLI path、alias、exposure 和导航 | 创建 Cobra 命令/flag、参数、安全、endpoint/token |
|
||||
| reviewed manual command additions | 将一个精确存在的 runnable Cobra leaf 合并进 `EffectiveCommandRegistry`;必须 reviewed 且带 reason | 运行时 fallback、覆盖冲突 identity、创建命令 |
|
||||
| Go/Cobra | 路径是否真实可执行、Cobra 接受的 flag、CLI 类型/默认值、执行校验、help 文本 | 稳定 canonical identity、Agent 场景选择、虚构 RPC |
|
||||
| Go/Cobra | Contract 编译后的可执行投影:路径是否真实可执行、Cobra 接受的 flag、DefValue、help 文本 | 稳定 canonical identity、Agent 场景选择、虚构 RPC;**不得**成为与 Contract 平行的第二套 flag 权威 |
|
||||
| native Schema identity annotations | implementation-side consistency evidence;存在时必须与 `EffectiveCommandRegistry` 精确一致 | 提供、补全、推断或覆盖 identity |
|
||||
| `schema_hints/metadata/*.json` parameter overlays | 精确覆盖现有 flag 的描述、映射、类型和 required 语义;并承载 safety / `runtime_gate` / interface | 创建命令/flag、绕过 completeness、虚构 RPC |
|
||||
| typed parameter metadata / constraints | `required_when`、one-of、互斥、联动、格式、枚举、位置参数 | 命令 identity |
|
||||
| `schema_parameter_bindings.json` | 稳定 CLI flag 到 RPC property 的映射 | 命令发现、risk 推断 |
|
||||
| `schema_mcp_metadata.json` | pinned RPC identity、接口描述和脱敏参数事实 | CLI identity、运行时路由、risk 推断 |
|
||||
| `schema_hints/metadata/*.json` parameter overlays | 补充 flag **description** 等文案;迁移期可读 safety | 创建命令/flag;**不得**静默改写 type/required/default(与 Contract 冲突时 Contract/cobra 胜);不得绕过 completeness、虚构 RPC |
|
||||
| typed parameter metadata / constraints | 由 Contract 约束投影而来的 `require_one_of` / 互斥等;以及仍需 reviewed 的 `required_when` 等 | 命令 identity |
|
||||
| `schema_parameter_bindings.json` | 稳定 CLI flag 到 RPC property 的映射 | 命令发现、risk 推断、创建 CLI flag |
|
||||
| `schema_mcp_metadata.json` | pinned RPC identity、接口描述和脱敏参数事实(interface 同源) | CLI identity、运行时路由、**创建 CLI flag**、risk 推断 |
|
||||
| `schema_hints/selection/*.json` | reviewed selection prose(summary / use_when / avoid_when / examples) | 创建 Cobra 命令或参数、改写 safety |
|
||||
| Skills/Markdown | 产品路由、工作流和使用建议 | 命令存在性和 flag 事实 |
|
||||
| `schema_catalog/`(catalog.json + tools/<product>.json)及其他 generated JSON | resolved registry 的兼容发布序列化;运行时由 production loader 解回 typed registry/index | generation/source resolution 输入、identity fallback、手工修复源 |
|
||||
|
||||
`schema_command_registry.json` 承载 reviewed `CommandRegistry`。Manual command addition 先以确定性规则合并进 effective registry;从 binder 开始,下游只看到一个稳定 identity/navigation 模型。旧 wire 中的 `surface_hash` / `surface_tools` 字段仅为兼容名称,语义已经是 effective Registry hash/coverage,不构成第二事实源。
|
||||
|
||||
### 4.1 flag / help / schema 同源(路径 A + 嵌入)
|
||||
|
||||
完整决策、字段归属表、`HOM-*` 门禁规划与可选 MCP 透传准入条件见 [`flag-help-schema-homology.md`](flag-help-schema-homology.md)。
|
||||
|
||||
摘要:
|
||||
|
||||
- **同源面**:Contract → cobra flags ≡ `--help` Flags ≡ **嵌入注解后的** schema `parameters` / 关系约束;显式 `Risk` 经 `dws.schema.risk` overlay 进 Schema Safety。
|
||||
- **嵌入点**:`command.embedContractIntoSchema` 写入 `dws.schema.contract` / property / type / required;`AnnotateConstraints` 写入 constraints;Schema 组装(`runtimeToolSpecFromMetadata`)消费这些注解并进入 `go:embed` catalog。
|
||||
- **硬规则**:CLI 表面事实 = **声明(Contract 数据字段)OR 人工标注**;禁止纯推断。非 CLI 表面字段(identity / selection / interface)必须有**评审源**。声明写法见同源文档 §1.2;标注见 §1.3;**`ToolSpec` 全字段权威见 RFC §5.0.4 / 同源 §1.4**。
|
||||
- **非同源面(有意)**:identity(registry)、selection 文案(hints/selection)、RPC 形状(MCP meta 仅 `interface_*`)、dry-run 正能力 registry。
|
||||
- **禁止**:以 MCP meta 为主通道生成 Leaf/Shortcut 的 flag;hints overlay 改写 type/required/default;Schema 字段无权威归属。
|
||||
|
||||
## 5. 统一解析与 precedence
|
||||
|
||||
### 5.1 Identity
|
||||
@@ -158,7 +175,7 @@ CI 同时禁止重新加入 generated native contracts 或 materialization 入
|
||||
|
||||
`schema_command_registry.json` 是 reviewed source,不是生成快照。它必须保留
|
||||
`$schema: ./schema_command_registry.schema.json`。该 JSON Schema 对 root、product
|
||||
和 CommandSpec 全部使用 `additionalProperties: false`,并约束:
|
||||
和 corecmd.Spec 全部使用 `additionalProperties: false`,并约束:
|
||||
|
||||
- canonical identity、`source_product_id` 和精确 CLI path 的格式;
|
||||
- `aliases` 唯一且不能复用 primary path;
|
||||
@@ -291,6 +308,7 @@ dws schema --all # 所有工具的完整 leaf 导
|
||||
- 每个 MCP `interface_ref` 必须在 pinned interface registry 精确存在;local/composite/unavailable 必须满足同一 conflict matrix。
|
||||
- `--all` 的 tool set 必须与最终 index 一对一,且每个工具包含完整参数契约。
|
||||
- 连续两次生成必须字节稳定,提交的生成物不得漂移。
|
||||
- **同源门禁(规划,见同源文档 §4)**:受管命令逐步满足 `HOM-P1`–`P3`(parameters ≡ cobra/Contract)、`HOM-S1`–`S3`(Safety/Risk 对齐)、`HOM-I1`(interface 不创建 flag)、`HOM-D1`(help ≡ schema parameters)。hints 不得作为 type/required/default 的 winner。
|
||||
|
||||
推荐本地验证:
|
||||
|
||||
@@ -306,8 +324,11 @@ go test ./internal/cli ./internal/app ./internal/generator/... -count=1
|
||||
|
||||
- 运行时调用 MCP `tools/list` 或访问网络生成 Schema。
|
||||
- 从 `schema_catalog/` 等生成 JSON 反向创建/补齐 Cobra leaf、flag、CommandRegistry 或下一轮 Catalog。
|
||||
- 从 `schema_mcp_metadata` **生成或补齐** LeafSpec/Shortcut 主路径的 CLI flag(interface overlay 除外);未满足同源文档 §5 准入条件时启用「MCP 透传生成通道」。
|
||||
- 把 native annotation、legacy registry 或 Catalog 当作 identity fallback;或在 `EffectiveCommandRegistry` 之后再次选择 identity winner。
|
||||
- renderer、query 或 gate 在 `SchemaRegistry` 之后重新读取 source 并做第二次 merge。
|
||||
- 用 prefix/wildcard exclusion 隐藏未来命令。
|
||||
- 让 manual hint、CommandRegistry 或 interface metadata 宣称一个不存在的命令、flag 或 RPC 可用。
|
||||
- 让 `schema_hints/metadata` overlay 在与 Contract/cobra 冲突时赢得 type/required/default。
|
||||
- 把 `schema --all` 当作普通业务数据查询,或把其完整结果无条件注入 Agent 上下文。
|
||||
- 将 LeafSpec、`+shortcut` 或 write-guard/cursor/多步命令注册为 `mcp_passthrough` 表面。
|
||||
|
||||
@@ -12,6 +12,7 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/itchyny/gojq v0.12.18
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/muesli/termenv v0.16.0
|
||||
github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.2-0.20260705041131-325e7c1049ad
|
||||
github.com/spf13/cobra v1.10.2
|
||||
@@ -40,7 +41,6 @@ require (
|
||||
github.com/itchyny/timefmt-go v0.1.7 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -64,6 +66,31 @@ func newAuditTailCommand() *cobra.Command {
|
||||
},
|
||||
}
|
||||
cmd.Flags().IntVarP(&n, "lines", "n", 20, "显示最近 N 条记录")
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "查看本地操作审计日志最近 N 条记录",
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "local", Availability: "available",
|
||||
Reason: "命令读取本地审计日志尾部,不绑定 pinned MCP RPC",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "查看本地操作审计日志最近 N 条记录",
|
||||
UseWhen: []string{"需要快速查看最近写入的审计记录(默认最近 20 条)"},
|
||||
AvoidWhen: []string{
|
||||
"需要按日期范围整段导出用 audit export",
|
||||
"需要校验哈希链用 audit verify",
|
||||
},
|
||||
Examples: []string{
|
||||
"dws audit tail",
|
||||
"dws audit tail --lines 50",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -99,6 +126,31 @@ func newAuditExportCommand() *cobra.Command {
|
||||
cmd.Flags().StringVar(&since, "since", "", "起始日期 (YYYY-MM-DD)")
|
||||
cmd.Flags().StringVar(&until, "until", "", "截止日期 (YYYY-MM-DD)")
|
||||
cmd.Flags().StringVar(&format, "format", "jsonl", "输出格式: jsonl 或 csv")
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "按日期范围导出本地操作审计日志(jsonl 或 csv)",
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "local", Availability: "available",
|
||||
Reason: "命令读取并导出本地审计日志文件,不绑定 pinned MCP RPC",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "按日期范围导出本地操作审计日志(jsonl 或 csv)",
|
||||
UseWhen: []string{"需要把本地审计日志导出为 jsonl/csv,或按 --since/--until 取一段时间"},
|
||||
AvoidWhen: []string{
|
||||
"只看最近几条用 audit tail",
|
||||
"只校验哈希链完整性用 audit verify",
|
||||
},
|
||||
Examples: []string{
|
||||
"dws audit export --format jsonl",
|
||||
"dws audit export --since 2026-07-01 --until 2026-07-14 --format csv",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -152,6 +204,28 @@ func newAuditVerifyCommand() *cobra.Command {
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&file, "file", "", "指定审计文件路径(默认最新文件)")
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "校验本地审计日志文件的哈希链完整性",
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "local", Availability: "available",
|
||||
Reason: "命令校验本地审计日志哈希链,不绑定 pinned MCP RPC",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "校验本地审计日志文件的哈希链完整性",
|
||||
UseWhen: []string{"怀疑审计文件被篡改,或需要确认最新/指定文件哈希链是否完整"},
|
||||
AvoidWhen: []string{"只浏览或导出日志内容时用 audit tail / audit export"},
|
||||
Examples: []string{
|
||||
"dws audit verify",
|
||||
"dws audit verify --file /path/to/audit.jsonl",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
)
|
||||
|
||||
func TestChatGroupCreateMembersGuidanceUsesGenericExample(t *testing.T) {
|
||||
root := NewRootCommand()
|
||||
cmd := mustFindCommand(t, root, "chat", "group", "create")
|
||||
err := enrichChatWorkbookError(cmd, stderrors.New("unknown flag: --members"))
|
||||
|
||||
var typed *apperrors.Error
|
||||
if !stderrors.As(err, &typed) {
|
||||
t.Fatalf("error = %T, want *errors.Error", err)
|
||||
}
|
||||
joined := strings.Join(typed.Examples, "\n")
|
||||
for _, unwanted := range []string{"V2评审小组", "489149", "550582"} {
|
||||
if strings.Contains(joined, unwanted) {
|
||||
t.Fatalf("example is fitted to evaluation data %q: %s", unwanted, joined)
|
||||
}
|
||||
}
|
||||
for _, want := range []string{"<群名称>", "<userId1>,<userId2>"} {
|
||||
if !strings.Contains(joined, want) {
|
||||
t.Fatalf("generic example missing %q: %s", want, joined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatHintPathsAreReportedAsUnknownSubcommandsBeforeFlagParsing(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
args []string
|
||||
message string
|
||||
flag string
|
||||
}{
|
||||
{name: "group search", args: []string{"chat", "group", "search", "--query", "1", "--format", "json"}, message: "chat group 下不存在 search 子命令", flag: "--query"},
|
||||
{name: "send", args: []string{"chat", "send", "--group", "cid", "--text", "hi", "--format", "json"}, message: "chat 下不存在 send 子命令", flag: "--group"},
|
||||
{name: "history", args: []string{"chat", "history", "--group", "cid", "--time", "2026-08-03 10:00:00", "--format", "json"}, message: "chat 下不存在 history 子命令", flag: "--group"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := validateChatWorkbookRawArgs(tc.args)
|
||||
var typed *apperrors.Error
|
||||
if !stderrors.As(err, &typed) {
|
||||
t.Fatalf("error = %T, want *errors.Error", err)
|
||||
}
|
||||
if typed.Message != tc.message || typed.Reason != "unknown_subcommand" {
|
||||
t.Fatalf("guidance = %#v", typed)
|
||||
}
|
||||
if strings.Contains(typed.Message, tc.flag) || strings.Contains(typed.Reason, "unknown flag") {
|
||||
t.Fatalf("subcommand error was misreported as a flag error: %#v", typed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
root := NewRootCommand()
|
||||
chat := mustFindCommand(t, root, "chat")
|
||||
for _, child := range chat.Commands() {
|
||||
if child.Name() == "send" || child.Name() == "history" {
|
||||
t.Fatalf("chat %s must not be registered as a hint subcommand", child.Name())
|
||||
}
|
||||
}
|
||||
group := mustFindCommand(t, root, "chat", "group")
|
||||
for _, child := range group.Commands() {
|
||||
if child.Name() == "search" {
|
||||
t.Fatal("chat group search must not be registered as a hint subcommand")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateChatWorkbookRawArgs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "members group flag",
|
||||
args: []string{"chat", "group", "members", "list", "--group", "cid-demo", "--format", "json"},
|
||||
want: "群成员列表命令路径或群参数不正确",
|
||||
},
|
||||
{
|
||||
name: "rename group flag",
|
||||
args: []string{"chat", "group", "rename", "--group=cid-demo", "--name", "新群名"},
|
||||
want: "群重命名命令不支持 --group",
|
||||
},
|
||||
{
|
||||
name: "image local path",
|
||||
args: []string{"chat", "message", "send", "--group", "cid-demo", "--msg-type", "image", "--file-path", "/tmp/x.png"},
|
||||
want: "image 消息不能直接使用 --file-path",
|
||||
},
|
||||
{
|
||||
name: "unsupported message type",
|
||||
args: []string{"chat", "message", "send", "--group", "cid-demo", "--msg-type=sticker"},
|
||||
want: "不支持指定的 --msg-type:sticker",
|
||||
},
|
||||
{
|
||||
name: "numeric group id required",
|
||||
args: []string{"chat", "group", "get-by-group-id", "--group-id", "cid-demo"},
|
||||
want: "--group-id 必须是数字群号",
|
||||
},
|
||||
{
|
||||
name: "file media id conflict",
|
||||
args: []string{"chat", "message", "send", "--group", "cid-demo", "--msg-type", "file", "--media-id", "media"},
|
||||
want: "文件消息不能使用 --media-id",
|
||||
},
|
||||
{
|
||||
name: "silent text media conflict",
|
||||
args: []string{"chat", "message", "send", "--group", "cid-demo", "--media-id", "media", "--text", "file.pdf"},
|
||||
want: "检测到 --media-id,但没有指定媒体消息类型",
|
||||
},
|
||||
{
|
||||
name: "dismiss numeric group id",
|
||||
args: []string{"chat", "group", "dismiss", "--group", "12345678"},
|
||||
want: "解散群命令需要 openConversationId,不是数字群号",
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := validateChatWorkbookRawArgs(tc.args)
|
||||
var typed *apperrors.Error
|
||||
if !stderrors.As(err, &typed) {
|
||||
t.Fatalf("error = %T, want *errors.Error", err)
|
||||
}
|
||||
if typed.Message != tc.want || len(typed.Actions) == 0 || len(typed.Examples) == 0 {
|
||||
t.Fatalf("guidance = %#v", typed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if err := validateChatWorkbookRawArgs([]string{"chat", "group", "rename", "--id", "cid-demo"}); err != nil {
|
||||
t.Fatalf("canonical rename args rejected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatWorkbookHelpGuidanceCoverage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, path := range []string{
|
||||
"chat group members",
|
||||
"chat group members add",
|
||||
"chat group members remove",
|
||||
"chat group members add-bot",
|
||||
"chat group members remove-bot",
|
||||
"chat group members list-by-ids",
|
||||
"chat group create",
|
||||
"chat group rename",
|
||||
"chat message list",
|
||||
"chat message search",
|
||||
"chat message search-advanced",
|
||||
"chat message list-all",
|
||||
"chat message list-by-sender",
|
||||
} {
|
||||
guide, ok := chatWorkbookHelpGuidance[path]
|
||||
if !ok || guide.reason == "" || guide.action == "" || guide.example == "" {
|
||||
t.Fatalf("incomplete help guidance for %q: %#v", path, guide)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawArgsFlagValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := rawArgsFlagValue([]string{"--msg-type", "image"}, "msg-type"); got != "image" {
|
||||
t.Fatalf("separate value = %q", got)
|
||||
}
|
||||
if got := rawArgsFlagValue([]string{"--msg-type=file"}, "msg-type"); got != "file" {
|
||||
t.Fatalf("equals value = %q", got)
|
||||
}
|
||||
if got := rawArgsFlagValue([]string{"--text", "hello"}, "msg-type"); got != "" {
|
||||
t.Fatalf("missing value = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawArgsRequestJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, args := range [][]string{
|
||||
{"chat", "search", "--format", "json"},
|
||||
{"chat", "search", "--format=json"},
|
||||
{"chat", "search", "-f", "JSON"},
|
||||
{"chat", "search", "-f=json"},
|
||||
} {
|
||||
if !rawArgsRequestJSON(args) {
|
||||
t.Fatalf("rawArgsRequestJSON(%v) = false", args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuppressJSONDeprecationPreamble(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := NewRootCommand()
|
||||
cmd := mustFindCommand(t, root, "chat", "media", "upload")
|
||||
if cmd.Deprecated == "" {
|
||||
t.Fatal("fixture command is not deprecated")
|
||||
}
|
||||
suppressJSONDeprecationPreamble(root, []string{"chat", "media", "upload", "--format", "json"})
|
||||
if cmd.Deprecated != "" {
|
||||
t.Fatalf("JSON execution kept deprecation preamble: %q", cmd.Deprecated)
|
||||
}
|
||||
|
||||
plainRoot := NewRootCommand()
|
||||
plain := mustFindCommand(t, plainRoot, "chat", "media", "upload")
|
||||
suppressJSONDeprecationPreamble(plainRoot, []string{"chat", "media", "upload"})
|
||||
if plain.Deprecated == "" {
|
||||
t.Fatal("human execution unexpectedly removed deprecation metadata")
|
||||
}
|
||||
}
|
||||
+123
-34
@@ -18,6 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
@@ -31,7 +32,6 @@ import (
|
||||
|
||||
authpkg "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/auth"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
dwsevent "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/bus"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/busctl"
|
||||
@@ -361,6 +361,38 @@ SIGTERM、关 stdin,或先用 dws event stop <subscribe_id> --dry-run 预览
|
||||
Variadic: true,
|
||||
Index: 0,
|
||||
})
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "write", Risk: "medium",
|
||||
Confirmation: "not_required", Idempotency: "non_idempotent",
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "订阅并持续消费一个或多个兼容的个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "composite", Availability: "available",
|
||||
Reason: "Reviewed composite workflow: the command creates or reuses a remote personal-event subscription and coordinates the local event bus and Stream consumer; no single pinned RPC represents the workflow.",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "订阅并持续消费一个或多个兼容的个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
|
||||
UseWhen: []string{
|
||||
"需要实时监听 @我、指定单聊、指定群或指定发送人的后续消息事件",
|
||||
"用户明确要求监听当前身份的所有单聊或所有群消息",
|
||||
"需要监听指定单聊或群聊中的消息已读、撤回或表情回应事件",
|
||||
"需要监听指定群的标题变更、成员进退群或群解散事件",
|
||||
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标",
|
||||
"同一目标、同一过滤条件需要同时监听多个兼容事件",
|
||||
},
|
||||
AvoidWhen: []string{
|
||||
"只查历史聊天记录时用 chat 查询命令",
|
||||
"只看事件目录/字段时用 event list / event schema",
|
||||
},
|
||||
Examples: []string{
|
||||
"dws event consume user_im_message_receive_user --open-dingtalk-id open-example --flatten --max-events 1 --format ndjson",
|
||||
"dws event consume user_im_message_receive_o2o user_im_message_read_o2o --user test-user-001 --flatten --max-events 2 --format ndjson",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -728,6 +760,28 @@ func newEventListCommand() *cobra.Command {
|
||||
cmd.Flags().BoolVar(&enabledOnly, "enabled-only", false, "个人事件目录只显示 enabled")
|
||||
cmd.Flags().BoolVar(&includePending, "include-pending", false, "个人事件目录包含 pending 项")
|
||||
hideEventInternalFlags(cmd, "as", "all", "all-editions", "client-id")
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "列出支持的个人事件目录与状态说明",
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "local", Availability: "available",
|
||||
Reason: "命令读取 CLI 内置的个人事件目录,不绑定 pinned MCP RPC",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "列出支持的个人事件目录与状态说明",
|
||||
UseWhen: []string{"尚不知道可用 event_key,需要先盘点个人事件目录"},
|
||||
AvoidWhen: []string{
|
||||
"已知 event_key 要看 payload 字段时用 event schema",
|
||||
"要开始监听时用 event consume",
|
||||
},
|
||||
Examples: []string{"dws event list --format json"},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -796,6 +850,25 @@ func newEventStatusCommand() *cobra.Command {
|
||||
cmd.Flags().StringVar(&personalOpts.StreamSourceID, "stream-source-id", strings.TrimSpace(os.Getenv("DWS_STREAM_SOURCE_ID")),
|
||||
"个人事件 sourceId;开源版默认 open,可由 edition 覆盖")
|
||||
hideEventInternalFlags(cmd, "as", "all", "all-editions", "client-id", "fail-on-orphan")
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "查看个人事件订阅、本地 bus 与消费进程状态",
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "composite", Availability: "available",
|
||||
Reason: "Reviewed composite workflow: the command reads the remote personal-event subscription control plane and combines it with local bus and consumer state; no single pinned RPC represents the result.",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "查看个人事件订阅、本地 bus 与消费进程状态",
|
||||
UseWhen: []string{"需要确认订阅是否活跃、bus/consume 是否仍在运行"},
|
||||
AvoidWhen: []string{"取消订阅用 event stop;列事件目录用 event list"},
|
||||
Examples: []string{"dws event status --format json"},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -1037,39 +1110,22 @@ func newEventStopCommand() *cobra.Command {
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
// Local validation runs in DeclareLeafMetadata Validate (PreRunE)
|
||||
// before ConfirmSafety. Confirmation is the framework wrap.
|
||||
as, err := eventNormalizeAs(asIdentity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if as == "user" {
|
||||
opts.SubscribeID = firstArg(args)
|
||||
hasSubscribeID := strings.TrimSpace(opts.SubscribeID) != ""
|
||||
if hasSubscribeID && opts.All {
|
||||
return fmt.Errorf("event stop --as user: subscribe_id and --all are mutually exclusive")
|
||||
}
|
||||
if !hasSubscribeID && !opts.All {
|
||||
return fmt.Errorf("event stop --as user: subscribe_id is required unless --all is set")
|
||||
}
|
||||
if eventStopDryRun(c) {
|
||||
return writeEventStopDryRun(c, as, opts)
|
||||
}
|
||||
if !eventStopConfirmed(c) {
|
||||
return eventStopConfirmationRequired("event stop 会取消个人事件订阅并停止本地消费")
|
||||
}
|
||||
return eventRunPersonalStop(c, opts)
|
||||
}
|
||||
if err := rejectChangedFlags(c, "user", "all", "personal-event-base-url", "stream-source-id"); err != nil {
|
||||
return fmt.Errorf("event stop: %w", err)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("event stop: subscribe_id is only supported with --as user")
|
||||
}
|
||||
if eventStopDryRun(c) {
|
||||
return writeEventStopDryRun(c, as, opts)
|
||||
}
|
||||
if !eventStopConfirmed(c) {
|
||||
return eventStopConfirmationRequired("event stop 会停止事件消费")
|
||||
}
|
||||
configDir := defaultConfigDir()
|
||||
clientID, _, _, _, err := eventResolveAppCredentials(configDir)
|
||||
if err != nil {
|
||||
@@ -1102,6 +1158,53 @@ func newEventStopCommand() *cobra.Command {
|
||||
Required: false,
|
||||
Index: 0,
|
||||
})
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "destructive", Risk: "high",
|
||||
Confirmation: "user_required", Idempotency: "unknown",
|
||||
},
|
||||
Validate: func(c *cobra.Command, args []string) error {
|
||||
as, err := eventNormalizeAs(asIdentity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if as == "user" {
|
||||
subscribeID := firstArg(args)
|
||||
hasSubscribeID := strings.TrimSpace(subscribeID) != ""
|
||||
if hasSubscribeID && opts.All {
|
||||
return fmt.Errorf("event stop --as user: subscribe_id and --all are mutually exclusive")
|
||||
}
|
||||
if !hasSubscribeID && !opts.All {
|
||||
return fmt.Errorf("event stop --as user: subscribe_id is required unless --all is set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := rejectChangedFlags(c, "user", "all", "personal-event-base-url", "stream-source-id"); err != nil {
|
||||
return fmt.Errorf("event stop: %w", err)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("event stop: subscribe_id is only supported with --as user")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "取消个人事件订阅并停止对应本地消费",
|
||||
DryRun: &helpers.LeafDryRunDecl{PreviewKind: "request", RemoteReads: false},
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "composite", Availability: "available",
|
||||
Reason: "Reviewed composite workflow: the command deletes remote personal-event subscriptions, interrupts local consumers, updates local state, and may stop the local bus; no single pinned RPC represents the workflow.",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "取消个人事件订阅并停止对应本地消费",
|
||||
UseWhen: []string{"用户明确要求取消已知 subscribe_id(或清理全部)并停止消费"},
|
||||
AvoidWhen: []string{
|
||||
"只需检查状态时用 event status",
|
||||
"目标订阅不明确或用户未确认时不要停止",
|
||||
},
|
||||
Examples: []string{"dws event stop SUBSCRIBE_ID --dry-run"},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -1110,20 +1213,6 @@ func eventStopDryRun(cmd *cobra.Command) bool {
|
||||
return value
|
||||
}
|
||||
|
||||
func eventStopConfirmed(cmd *cobra.Command) bool {
|
||||
value, _ := cmd.Flags().GetBool("yes")
|
||||
return value
|
||||
}
|
||||
|
||||
func eventStopConfirmationRequired(action string) error {
|
||||
return apperrors.NewValidation(
|
||||
action+";请先使用 --dry-run 预览,确认后加 --yes 执行",
|
||||
apperrors.WithReason("confirmation_required"),
|
||||
apperrors.WithHint("先以相同参数加 --dry-run 预览;获得用户确认后改用 --yes 执行"),
|
||||
apperrors.WithActions("使用 --dry-run 生成预览", "获得用户确认后使用 --yes 执行"),
|
||||
)
|
||||
}
|
||||
|
||||
func writeEventStopDryRun(cmd *cobra.Command, identity string, opts personalStopOptions) error {
|
||||
payload := map[string]any{
|
||||
"dry_run": true,
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -177,6 +178,28 @@ func newEventSchemaCommand() *cobra.Command {
|
||||
Required: true,
|
||||
Index: 0,
|
||||
})
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "查询指定个人事件码的输出字段结构;Agent 应查询 --flatten 模式",
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "local", Availability: "available",
|
||||
Reason: "命令读取 CLI 内置的个人事件 payload 定义,不绑定 pinned MCP RPC",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "查询指定个人事件码的输出字段结构;Agent 应查询 --flatten 模式",
|
||||
UseWhen: []string{"已知任一公开个人 IM event_key,消费前需要理解输出字段或保守 payload 契约"},
|
||||
AvoidWhen: []string{
|
||||
"查询 CLI 命令参数契约时用顶层 dws schema",
|
||||
"要实际收事件时用 event consume",
|
||||
},
|
||||
Examples: []string{"dws event schema user_im_message_receive_at --flatten --format json"},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package app
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
||||
"strings"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||||
@@ -81,6 +82,28 @@ func newMCPURLGetCommand(caller edition.ToolCaller) *cobra.Command {
|
||||
Required: true,
|
||||
Index: 0,
|
||||
})
|
||||
helpers.DeclareLeafMetadata(cmd, helpers.LeafSpec{
|
||||
Safety: cli.SafetySpec{
|
||||
Effect: "read", Risk: "medium",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Schema: helpers.LeafSchema{
|
||||
Description: "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
Interface: &helpers.LeafInterfaceDecl{
|
||||
Mode: "composite", Availability: "available",
|
||||
Reason: "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
},
|
||||
Selection: helpers.LeafSelectionDecl{
|
||||
AgentSummary: "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
UseWhen: []string{"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"},
|
||||
AvoidWhen: []string{
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播",
|
||||
},
|
||||
Examples: []string{"dws mcp url get 10043 --format json"},
|
||||
},
|
||||
},
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -113,16 +112,6 @@ func Execute() (exitCode int) {
|
||||
root := rootNewRootCommandWithEngine(ctx, engine)
|
||||
timing.Record("cmd_init", time.Since(initStart))
|
||||
|
||||
if err := validateChatWorkbookRawArgs(os.Args[1:]); err != nil {
|
||||
if rawArgsRequestJSON(os.Args[1:]) {
|
||||
_ = apperrors.PrintJSON(os.Stderr, err)
|
||||
} else {
|
||||
_ = apperrors.PrintHumanAt(os.Stderr, err, resolveVerbosity(root))
|
||||
}
|
||||
return apperrors.ExitCode(err)
|
||||
}
|
||||
suppressJSONDeprecationPreamble(root, os.Args[1:])
|
||||
|
||||
// Run PreParse handlers on raw argv before Cobra parses flags.
|
||||
// This corrects model-generated errors like --userId → --user-id
|
||||
// and --limit100 → --limit 100.
|
||||
@@ -138,7 +127,6 @@ func Execute() (exitCode int) {
|
||||
executed = root
|
||||
}
|
||||
err = rewordRequiredFlagError(err)
|
||||
err = enrichChatWorkbookError(executed, err)
|
||||
if isUnknownCommandError(err) {
|
||||
executed.SetOut(os.Stderr)
|
||||
_ = executed.Help()
|
||||
@@ -153,474 +141,6 @@ func Execute() (exitCode int) {
|
||||
return 0
|
||||
}
|
||||
|
||||
func suppressJSONDeprecationPreamble(root *cobra.Command, args []string) {
|
||||
if root == nil || !rawArgsRequestJSON(args) || len(args) < 3 {
|
||||
return
|
||||
}
|
||||
if args[0] != "chat" || args[1] != "media" || args[2] != "upload" {
|
||||
return
|
||||
}
|
||||
if cmd, _, err := root.Find([]string{"chat", "media", "upload"}); err == nil && cmd != nil {
|
||||
cmd.Deprecated = ""
|
||||
}
|
||||
}
|
||||
|
||||
func validateChatWorkbookRawArgs(args []string) error {
|
||||
path := strings.Join(args, " ")
|
||||
switch {
|
||||
case len(args) >= 3 && args[0] == "chat" && args[1] == "group" && args[2] == "search":
|
||||
return apperrors.NewValidation(
|
||||
"chat group 下不存在 search 子命令",
|
||||
apperrors.WithReason("unknown_subcommand"),
|
||||
apperrors.WithActions("群聊搜索使用 dws chat search,而不是 dws chat group search", "移除路径中的 group 后重试"),
|
||||
apperrors.WithExamples(`dws chat search --query <群名关键词> --format json`),
|
||||
)
|
||||
case len(args) >= 2 && args[0] == "chat" && args[1] == "send":
|
||||
return apperrors.NewValidation(
|
||||
"chat 下不存在 send 子命令",
|
||||
apperrors.WithReason("unknown_subcommand"),
|
||||
apperrors.WithActions("发送消息使用 dws chat message send,而不是 dws chat send", "在路径中补充 message 后重试"),
|
||||
apperrors.WithExamples(`dws chat message send --group <openConversationId> --text <消息正文> --format json`),
|
||||
)
|
||||
case len(args) >= 2 && args[0] == "chat" && args[1] == "history":
|
||||
return apperrors.NewValidation(
|
||||
"chat 下不存在 history 子命令",
|
||||
apperrors.WithReason("unknown_subcommand"),
|
||||
apperrors.WithActions("查询会话消息使用 dws chat message list,而不是 dws chat history", "改用 message list 并按帮助补充目标和时间参数"),
|
||||
apperrors.WithExamples(`dws chat message list --group <openConversationId> --time <YYYY-MM-DD HH:mm:ss> --format json`),
|
||||
)
|
||||
case strings.HasPrefix(path, "chat message send ") && rawArgsFlagValue(args, "msg-type") == "file" &&
|
||||
rawArgsContainFlag(args, "media-id"):
|
||||
return apperrors.NewValidation(
|
||||
"文件消息不能使用 --media-id",
|
||||
apperrors.WithReason("PDF、DOCX、XLSX 和本地图片等文件通过 --file-path 上传发送;mediaId 仅用于已有媒体标识的 image 消息"),
|
||||
apperrors.WithActions("移除 --media-id", "补充 --file-path 并保留 --msg-type file"),
|
||||
apperrors.WithExamples(`dws chat message send --group <openConversationId> --msg-type file --file-path ./report.pdf --format json`),
|
||||
)
|
||||
case strings.HasPrefix(path, "chat message send ") && rawArgsContainFlag(args, "media-id") &&
|
||||
rawArgsFlagValue(args, "msg-type") == "":
|
||||
return apperrors.NewValidation(
|
||||
"检测到 --media-id,但没有指定媒体消息类型",
|
||||
apperrors.WithReason("未指定 --msg-type 时命令会进入文本分支,可能把文件名当成普通文字发送"),
|
||||
apperrors.WithActions("已有图片 mediaId 时补充 --msg-type image", "发送 PDF/DOCX/XLSX 时移除 --media-id,改用 --msg-type file --file-path"),
|
||||
apperrors.WithExamples(`dws chat message send --group <openConversationId> --msg-type image --media-id <mediaId> --format json`, `dws chat message send --group <openConversationId> --msg-type file --file-path ./thesis.pdf --format json`),
|
||||
)
|
||||
case strings.HasPrefix(path, "chat message send ") && rawArgsFlagValue(args, "msg-type") == "image" &&
|
||||
rawArgsContainFlag(args, "file-path") && !rawArgsContainFlag(args, "media-id"):
|
||||
filePath := rawArgsFlagValue(args, "file-path")
|
||||
return apperrors.NewValidation(
|
||||
"image 消息不能直接使用 --file-path",
|
||||
apperrors.WithReason("msg-type=image 只接受已有 mediaId;本地图片路径不能自动转换为 mediaId"),
|
||||
apperrors.WithActions("发送本地图片时改用 --msg-type file", "保留原路径并通过 --file-path 发送为文件附件"),
|
||||
apperrors.WithExamples(fmt.Sprintf(`dws chat message send --group <openConversationId> --msg-type file --file-path %q --format json`, filePath)),
|
||||
)
|
||||
case strings.HasPrefix(path, "chat message send ") && rawArgsFlagValue(args, "msg-type") == "image" &&
|
||||
!rawArgsContainFlag(args, "media-id"):
|
||||
return apperrors.NewValidation(
|
||||
"图片消息缺少 --media-id",
|
||||
apperrors.WithReason("msg-type=image 只接受上游已经获得的有效 mediaId,不能把本地文件名当作 mediaId"),
|
||||
apperrors.WithActions("已有 mediaId 时补充 --media-id", "发送本地图片时改用 --msg-type file --file-path"),
|
||||
apperrors.WithExamples(`dws chat message send --group <openConversationId> --msg-type image --media-id <mediaId> --format json`, `dws chat message send --group <openConversationId> --msg-type file --file-path ./image.png --format json`),
|
||||
)
|
||||
case strings.HasPrefix(path, "chat message send "):
|
||||
msgType := rawArgsFlagValue(args, "msg-type")
|
||||
switch msgType {
|
||||
case "", "text", "markdown", "image", "file", "audio", "video", "location", "profile":
|
||||
default:
|
||||
return apperrors.NewValidation(
|
||||
"不支持指定的 --msg-type:"+msgType,
|
||||
apperrors.WithReason("当前命令不支持 sticker/card 等消息类型;文本或 Markdown 消息无需传 --msg-type"),
|
||||
apperrors.WithActions("文本消息移除 --msg-type 并使用 --text", "媒体消息使用 image、file、audio、video、location 或 profile"),
|
||||
apperrors.WithExamples(`dws chat message send --group <openConversationId> --text "hi" --format json`),
|
||||
)
|
||||
}
|
||||
case strings.HasPrefix(path, "chat group get-by-group-id "):
|
||||
value := rawArgsFlagValue(args, "group-id")
|
||||
if value != "" {
|
||||
if _, err := strconv.ParseInt(value, 10, 64); err != nil {
|
||||
return apperrors.NewValidation(
|
||||
"--group-id 必须是数字群号",
|
||||
apperrors.WithReason("cid 开头的值是 openConversationId,不是 get-by-group-id 所需的数字群号"),
|
||||
apperrors.WithActions("如果已有 openConversationId,请改用接受 --group 的群查询命令", "只有拿到数字群号时才调用 get-by-group-id"),
|
||||
apperrors.WithExamples(`dws chat group get-by-group-id --group-id 12345678 --format json`),
|
||||
)
|
||||
}
|
||||
}
|
||||
case strings.HasPrefix(path, "chat group dismiss ") && rawArgsContainFlag(args, "group"):
|
||||
value := rawArgsFlagValue(args, "group")
|
||||
if _, err := strconv.ParseInt(value, 10, 64); err == nil {
|
||||
return apperrors.NewValidation(
|
||||
"解散群命令需要 openConversationId,不是数字群号",
|
||||
apperrors.WithReason("--group 应传 cid 开头或服务端返回的 openConversationId;数字群号只用于 get-by-group-id"),
|
||||
apperrors.WithActions("先通过 chat search 获取 openConversationId", "确认目标群及不可逆影响后再执行解散"),
|
||||
apperrors.WithExamples(`dws chat group dismiss --group <openConversationId> --format json`),
|
||||
)
|
||||
}
|
||||
case strings.HasPrefix(path, "chat group members ") && rawArgsContainFlag(args, "group"):
|
||||
return apperrors.NewValidation(
|
||||
"群成员列表命令路径或群参数不正确",
|
||||
apperrors.WithReason("群成员列表的可执行命令是 chat group members,群 ID 参数名为 --id;不存在 members list --group 这一组合"),
|
||||
apperrors.WithActions("移除多余的 list 子命令", "将 --group 改为 --id"),
|
||||
apperrors.WithExamples(`dws chat group members --id <openConversationId> --format json`),
|
||||
)
|
||||
case strings.HasPrefix(path, "chat group rename ") && rawArgsContainFlag(args, "group"):
|
||||
return apperrors.NewValidation(
|
||||
"群重命名命令不支持 --group",
|
||||
apperrors.WithReason("chat group rename 使用 --id 接收群 openConversationId,而不是 --group"),
|
||||
apperrors.WithActions("将 --group 改为 --id", "群 ID 不确定时先用 chat search 查询"),
|
||||
apperrors.WithExamples(`dws chat group rename --id <openConversationId> --name "新群名" --format json`),
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rawArgsContainFlag(args []string, name string) bool {
|
||||
prefix := "--" + name
|
||||
for _, arg := range args {
|
||||
if arg == prefix || strings.HasPrefix(arg, prefix+"=") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func rawArgsFlagValue(args []string, name string) string {
|
||||
prefix := "--" + name
|
||||
for i, arg := range args {
|
||||
if strings.HasPrefix(arg, prefix+"=") {
|
||||
return strings.TrimPrefix(arg, prefix+"=")
|
||||
}
|
||||
if arg == prefix && i+1 < len(args) {
|
||||
return args[i+1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func rawArgsRequestJSON(args []string) bool {
|
||||
for i, arg := range args {
|
||||
if arg == "--format=json" || arg == "-f=json" {
|
||||
return true
|
||||
}
|
||||
if (arg == "--format" || arg == "-f") && i+1 < len(args) && strings.EqualFold(args[i+1], "json") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type chatWorkbookGuidance struct {
|
||||
message string
|
||||
reason string
|
||||
actions []string
|
||||
examples []string
|
||||
}
|
||||
|
||||
var chatRequiredGuidance = map[string]chatWorkbookGuidance{
|
||||
"chat message send-by-webhook": {
|
||||
"Webhook 发送参数不完整",
|
||||
"Webhook 消息必须同时提供机器人地址中的 access_token、标题和正文;不能降级为普通群消息",
|
||||
[]string{"从自定义机器人 Webhook 地址提取 token", "同时补齐 --title 和 --text,并确保包含机器人安全关键词"},
|
||||
[]string{`dws chat message send-by-webhook --token <access_token> --title "dws测试通知" --text "dws测试:评测结果已出" --format json`},
|
||||
},
|
||||
"chat group rename": {
|
||||
"群重命名缺少群 ID 或新名称", "--id 必须是群 openConversationId,--name 是新的群名称",
|
||||
[]string{"先用 chat search 获取群 openConversationId", "同时提供 --id 和 --name"},
|
||||
[]string{`dws chat group rename --id <openConversationId> --name "新群名" --format json`},
|
||||
},
|
||||
"chat group dismiss": {
|
||||
"解散群缺少目标群 ID", "解散群不可逆且需要群主权限,--group 必须是 openConversationId",
|
||||
[]string{"先确认目标群和影响范围", "获取 openConversationId 后再执行,并按运行时要求确认"},
|
||||
[]string{`dws chat group dismiss --group <openConversationId> --format json`},
|
||||
},
|
||||
"chat group quit": {
|
||||
"退出群缺少目标群 ID", "quit 表示当前用户退出群聊,不会解散整个群;--group 必须是 openConversationId",
|
||||
[]string{"确认你要退出而不是解散群", "先获取目标群 openConversationId"},
|
||||
[]string{`dws chat group quit --group <openConversationId> --format json`},
|
||||
},
|
||||
"chat group set-admin": {
|
||||
"设置群管理员参数不完整", "需要目标群以及一个或多个成员;默认设为管理员,--off 表示取消管理员",
|
||||
[]string{"补充 --group", "通过 --user 或 --users 指定成员,取消管理员时增加 --off"},
|
||||
[]string{`dws chat group set-admin --group <openConversationId> --users <userId1>,<userId2> --format json`},
|
||||
},
|
||||
"chat group transfer-owner": {
|
||||
"转让群主参数不完整", "--group 指定群,--new-owner 使用 openDingTalkId,--user 使用 userId",
|
||||
[]string{"补充群 openConversationId", "在 --new-owner 和 --user 中选择一个新群主标识"},
|
||||
[]string{`dws chat group transfer-owner --group <openConversationId> --new-owner <openDingTalkId> --format json`},
|
||||
},
|
||||
"chat group update-nick": {
|
||||
"修改本人群昵称参数不完整", "update-nick 只修改当前登录用户在指定群里的昵称,需要群 ID 和新昵称",
|
||||
[]string{"补充 --group openConversationId", "补充新的昵称参数"},
|
||||
[]string{`dws chat group update-nick --group <openConversationId> --nick "新昵称" --format json`},
|
||||
},
|
||||
"chat group update-icon": {
|
||||
"更新群头像参数不完整", "需要群 openConversationId 和上游已经获得的有效图片 mediaId",
|
||||
[]string{"补充 --group", "从上游媒体能力获取 mediaId 后传入 --icon-media-id"},
|
||||
[]string{`dws chat group update-icon --group <openConversationId> --icon-media-id <mediaId> --format json`},
|
||||
},
|
||||
"chat group share-invite": {
|
||||
"分享群邀请参数不完整", "--source 是被分享群,--target 是接收分享的会话,--receiver 是接收分享的单聊用户",
|
||||
[]string{"补充 --source", "在 --target 和 --receiver 中选择一个接收目标"},
|
||||
[]string{`dws chat group share-invite --source <源群ID> --target <目标会话ID> --format json`},
|
||||
},
|
||||
"chat message reply": {
|
||||
"引用回复参数不完整", "会话 ID、原消息 ID、原发送者和回复正文必须来自或对应同一条原消息",
|
||||
[]string{"先拉取目标消息", "补齐 conversation-id、ref-msg-id、ref-sender 和 text"},
|
||||
[]string{`dws chat message reply --conversation-id <openConversationId> --ref-msg-id <openMessageId> --ref-sender <openDingTalkId> --text "收到" --format json`},
|
||||
},
|
||||
"chat message forward": {
|
||||
"转发消息参数不完整", "消息 ID 必须属于源会话,并需要明确源会话和目标会话",
|
||||
[]string{"先从源会话拉取真实消息 ID", "确认 src 和 dest 没有写反"},
|
||||
[]string{`dws chat message forward --src-conversation-id <源会话ID> --msg-id <openMessageId> --dest-conversation-id <目标会话ID> --format json`},
|
||||
},
|
||||
"chat message recall": {
|
||||
"撤回消息参数不完整", "用户消息撤回需要会话 ID 和本人发送的消息 ID;机器人消息应使用 recall-by-bot",
|
||||
[]string{"确认消息由当前用户发送", "补齐 conversation-id 和 msg-id"},
|
||||
[]string{`dws chat message recall --conversation-id <openConversationId> --msg-id <openMessageId> --format json`},
|
||||
},
|
||||
"chat message read-status": {
|
||||
"查询消息已读状态参数不完整", "只能查询当前用户发出消息的已读状态,需要会话和消息标识",
|
||||
[]string{"补齐会话和消息 ID", "人员筛选时区分 userId 与 openDingTalkId"},
|
||||
[]string{`dws chat message read-status --conversation-id <openConversationId> --message-id <openMessageId> --format json`},
|
||||
},
|
||||
"chat message list-by-ids": {
|
||||
"缺少消息 ID 列表", "--msg-ids 使用逗号分隔的真实 openMessageId,单次最多 50 条",
|
||||
[]string{"先拉取真实消息 ID", "将不超过 50 条 ID 用逗号连接"},
|
||||
[]string{`dws chat message list-by-ids --msg-ids <id1>,<id2> --format json`},
|
||||
},
|
||||
"chat message download-media": {
|
||||
"媒体下载参数不完整", "type、resource-id、message-id、open-conversation-id 和 output 必须完整,且资源与消息来自同一条消息",
|
||||
[]string{"先拉取目标媒体消息", "从同一条消息取得资源、消息和会话标识"},
|
||||
[]string{`dws chat message download-media --type mediaId --resource-id <mediaId> --message-id <openMessageId> --open-conversation-id <openConversationId> --output ./downloads/ --format json`},
|
||||
},
|
||||
"chat message add-emoji": {
|
||||
"添加表情回应参数不完整", "需要真实会话 ID、消息 ID 和 emoji 名称",
|
||||
[]string{"先拉取目标消息", "补齐 conversation-id、msg-id 和 emoji"},
|
||||
[]string{`dws chat message add-emoji --conversation-id <openConversationId> --msg-id <openMessageId> --emoji "赞" --format json`},
|
||||
},
|
||||
"chat message remove-emoji": {
|
||||
"移除表情回应参数不完整", "只能移除当前用户已添加的同名回应,需要会话、消息和 emoji 名称完全匹配",
|
||||
[]string{"确认当前用户添加过该回应", "补齐 conversation-id、msg-id 和 emoji"},
|
||||
[]string{`dws chat message remove-emoji --conversation-id <openConversationId> --msg-id <openMessageId> --emoji "赞" --format json`},
|
||||
},
|
||||
"chat message list-by-sender": {
|
||||
"按发送者查询参数不完整", "必须提供开始时间以及发送者 userId/openDingTalkId 二选一,可选 end 和 cursor",
|
||||
[]string{"补充 --start", "在 sender-user-id 和 sender-open-dingtalk-id 中选择一个"},
|
||||
[]string{`dws chat message list-by-sender --sender-user-id <userId> --start "2026-07-14T00:00:00+08:00" --format json`},
|
||||
},
|
||||
"chat message query-send-status": {
|
||||
"缺少发送任务 ID", "--open-task-id 来自 message send 返回的 openTaskId,不是消息 ID",
|
||||
[]string{"先执行 message send", "从发送结果读取 openTaskId"},
|
||||
[]string{`dws chat message query-send-status --open-task-id <openTaskId> --format json`},
|
||||
},
|
||||
}
|
||||
|
||||
func enrichChatWorkbookError(cmd *cobra.Command, err error) error {
|
||||
if cmd == nil || err == nil {
|
||||
return err
|
||||
}
|
||||
path := cmd.CommandPath()
|
||||
if fields := strings.Fields(path); len(fields) > 1 {
|
||||
path = strings.Join(fields[1:], " ")
|
||||
}
|
||||
message := err.Error()
|
||||
var guide chatWorkbookGuidance
|
||||
switch {
|
||||
case path == "chat message send" &&
|
||||
(strings.Contains(message, "unknown flag: --at-user-ids") ||
|
||||
strings.Contains(message, "unknown flag: --at-users") ||
|
||||
strings.Contains(message, "unknown flag: --mention")):
|
||||
guide = chatWorkbookGuidance{
|
||||
"群消息 @成员参数不正确",
|
||||
"当前用户身份发送群消息时使用 --at-open-dingtalk-ids,参数值必须是成员的 openDingTalkId;--at-user-ids、--at-users、--mention 均不是有效参数",
|
||||
[]string{"先查询目标成员的 openDingTalkId", "改用 --at-open-dingtalk-ids,并在正文中写入 <@openDingTalkId>"},
|
||||
[]string{`dws chat message send --group <openConversationId> --at-open-dingtalk-ids <openDingTalkId> --text "<@openDingTalkId> 请关注" --format json`},
|
||||
}
|
||||
case path == "chat media upload":
|
||||
guide = chatWorkbookGuidance{
|
||||
"chat media upload 已下线",
|
||||
"当前 CLI 不再通过该命令把本地文件转换为 mediaId,本地图片和文件统一由 message send 的 file 路径上传并发送",
|
||||
[]string{"发送本地图片或文件时使用 --msg-type file --file-path", "只有上游已提供 mediaId 时才使用 --msg-type image --media-id"},
|
||||
[]string{`dws chat message send --group <openConversationId> --msg-type file --file-path ./image.png --format json`},
|
||||
}
|
||||
case path == "chat group members" && strings.Contains(message, "unknown flag: --group"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"群成员列表命令路径或群参数不正确",
|
||||
"群成员列表的可执行命令是 chat group members,群 ID 参数名为 --id;不存在 members list --group 这一组合",
|
||||
[]string{"移除多余的 list 子命令", "将 --group 改为 --id"},
|
||||
[]string{`dws chat group members --id <openConversationId> --format json`},
|
||||
}
|
||||
case path == "chat group rename" && strings.Contains(message, "unknown flag: --group"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"群重命名命令不支持 --group",
|
||||
"chat group rename 使用 --id 接收群 openConversationId,而不是 --group",
|
||||
[]string{"将 --group 改为 --id", "群 ID 不确定时先用 chat search 查询"},
|
||||
[]string{`dws chat group rename --id <openConversationId> --name "新群名" --format json`},
|
||||
}
|
||||
case path == "chat group create" && strings.Contains(message, "unknown flag: --members"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"建群命令不支持 --members",
|
||||
"chat group create 使用 --users 接收逗号分隔的成员 userId;--members 是其他命令的参数名",
|
||||
[]string{"将 --members 改为 --users", "成员标识不确定时先查询 userId"},
|
||||
[]string{`dws chat group create --name "<群名称>" --users <userId1>,<userId2> --format json`},
|
||||
}
|
||||
case path == "chat group bots" && strings.Contains(message, "unknown flag: --id"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"群机器人列表命令不支持 --id",
|
||||
"chat group bots 使用 --group 接收群 openConversationId;该参数名与 members、rename 命令不同",
|
||||
[]string{"将 --id 改为 --group", "群 ID 不确定时先用 chat search 查询"},
|
||||
[]string{`dws chat group bots --group <openConversationId> --format json`},
|
||||
}
|
||||
case path == "chat message list-mentions" && strings.Contains(message, "required flag"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"缺少必填参数:--start、--end",
|
||||
"查询 @我 消息必须同时提供 ISO-8601 格式的开始和结束时间;只提供分页参数不能确定查询范围",
|
||||
[]string{"同时补充 --start 和 --end,不要逐个参数反复试错", "按本地时区设置明确的查询时间窗"},
|
||||
[]string{`dws chat message list-mentions --start "2026-07-23T00:00:00+08:00" --end "2026-07-30T23:59:59+08:00" --limit 50 --format json`},
|
||||
}
|
||||
case path == "chat message send" && strings.Contains(message, "--group, --user or --open-dingtalk-id is required"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"缺少消息接收目标",
|
||||
"发送消息必须在 --group、--user、--open-dingtalk-id 中选择且只选择一个接收目标",
|
||||
[]string{"发群消息时先查询并传入群 openConversationId", "发单聊时先查询并传入 userId 或 openDingTalkId"},
|
||||
[]string{`dws chat message send --group <openConversationId> --text "评测消息" --format json`, `dws chat message send --open-dingtalk-id <openDingTalkId> --text "评测消息" --format json`},
|
||||
}
|
||||
case path == "chat search" && strings.Contains(message, "query"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"缺少群聊搜索关键词:--query",
|
||||
"群聊搜索需要关键词才能定位候选群,不能使用空查询",
|
||||
[]string{"使用 --query 传入群名称或名称片段", "从结果中读取 openConversationId 供后续群命令使用"},
|
||||
[]string{`dws chat search --query "项目群" --format json`},
|
||||
}
|
||||
case path == "chat message search-advanced":
|
||||
guide = chatWorkbookGuidance{
|
||||
"高级消息搜索至少需要一个搜索条件",
|
||||
"空条件搜索无法限定目标消息,必须提供关键词、人员、@我状态或会话范围中的至少一种",
|
||||
[]string{"按内容搜索时传入 --query", "也可通过 --user、--at-me 或 --conversation-ids 缩小范围"},
|
||||
[]string{`dws chat message search-advanced --query "评审" --format json`},
|
||||
}
|
||||
case path == "chat message search" && strings.Contains(message, "required flag"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"关键词消息搜索缺少完整查询条件",
|
||||
"关键词消息搜索需要 --query、--start 和 --end;当前命令没有提供完整的关键词和时间范围",
|
||||
[]string{"补充搜索关键词", "同时提供 ISO-8601 格式的开始和结束时间"},
|
||||
[]string{`dws chat message search --query "评审" --start "2026-07-23T00:00:00+08:00" --end "2026-07-30T23:59:59+08:00" --format json`},
|
||||
}
|
||||
case path == "chat message list-all" && strings.Contains(message, "required flag"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"跨会话消息查询缺少时间范围",
|
||||
"拉取全部会话消息必须使用 --start 和 --end 限定范围,避免无边界查询历史消息",
|
||||
[]string{"同时补充 --start 和 --end", "结果存在 hasMore 时使用 nextCursor 继续翻页"},
|
||||
[]string{`dws chat message list-all --start "2026-07-23T00:00:00+08:00" --end "2026-07-30T23:59:59+08:00" --limit 50 --format json`},
|
||||
}
|
||||
case path == "chat message list-topic-replies" && strings.Contains(message, "topic-id"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"缺少话题定位参数:--topic-id",
|
||||
"topic-id 不能臆造,必须来自同一群聊消息列表中目标话题消息的 openConvThreadId",
|
||||
[]string{"先执行 chat message list 拉取目标群消息", "从目标话题消息读取 openConvThreadId 并作为 --topic-id"},
|
||||
[]string{`dws chat message list --group <openConversationId> --time "2026-07-30 23:59:59" --direction older --format json`, `dws chat message list-topic-replies --group <openConversationId> --topic-id <openConvThreadId> --limit 50 --format json`},
|
||||
}
|
||||
case path == "chat message send-by-bot" && strings.Contains(message, "required flag"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"机器人发送消息缺少必填参数",
|
||||
"机器人发送需要 robotCode、标题、正文以及群聊或单聊目标,当前参数不完整",
|
||||
[]string{"补充 --robot-code 和 --title", "通过 --group 或用户参数指定接收目标"},
|
||||
[]string{`dws chat message send-by-bot --robot-code <robotCode> --group <openConversationId> --title "通知" --text "hello" --format json`},
|
||||
}
|
||||
case path == "chat message recall-by-bot" && strings.Contains(message, "required flag"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"机器人撤回消息缺少 robotCode 或 processQueryKey",
|
||||
"--keys 的 processQueryKey 来自机器人发送消息的返回结果,不能凭空构造",
|
||||
[]string{"补充发送该消息的 --robot-code", "从发送结果读取 processQueryKey 并传给 --keys"},
|
||||
[]string{`dws chat message recall-by-bot --robot-code <robotCode> --group <openConversationId> --keys <processQueryKey> --format json`},
|
||||
}
|
||||
case path == "chat message list" && strings.Contains(message, "required flag") && strings.Contains(message, "--time"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"拉取会话消息缺少时间锚点:--time",
|
||||
"消息列表按时间向前或向后拉取,必须提供一个明确的时间锚点",
|
||||
[]string{"补充格式为 YYYY-MM-DD HH:mm:ss 的 --time", "使用 --direction older 或 newer 明确查询方向"},
|
||||
[]string{`dws chat message list --group <openConversationId> --time "2026-07-30 10:00:00" --direction older --format json`},
|
||||
}
|
||||
case path == "chat message list" && strings.Contains(message, "--group, --user or --open-dingtalk-id is required"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"拉取消息时缺少会话目标",
|
||||
"必须在群聊 openConversationId、单聊 userId、单聊 openDingTalkId 中选择且只选择一个目标",
|
||||
[]string{"群聊先用 chat search 获取 openConversationId", "单聊先查询人员标识,再传 --user 或 --open-dingtalk-id"},
|
||||
[]string{`dws chat message list --group <openConversationId> --time "2026-07-15 10:00:00" --format json`},
|
||||
}
|
||||
case path == "chat message send" && strings.Contains(message, "media-id is required"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"图片消息缺少 --media-id",
|
||||
"msg-type=image 只接受上游已经获得的有效 mediaId,不能把本地文件名当作 mediaId",
|
||||
[]string{"已有 mediaId 时补充 --media-id", "发送本地图片时改用 --msg-type file --file-path"},
|
||||
[]string{`dws chat message send --group <openConversationId> --msg-type image --media-id <mediaId> --format json`, `dws chat message send --group <openConversationId> --msg-type file --file-path ./image.png --format json`},
|
||||
}
|
||||
case path == "chat message send" && strings.Contains(message, "readable local --file-path is required"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"文件消息缺少可读的本地文件",
|
||||
"file、audio、video 消息需要可读的 --file-path;旧版 dentry 参数则必须成组提供",
|
||||
[]string{"优先传入当前机器上可读的 --file-path", "使用旧参数时同时提供 dentry-id、space-id 和 file-name"},
|
||||
[]string{`dws chat message send --group <openConversationId> --msg-type file --file-path ./report.pdf --format json`},
|
||||
}
|
||||
case path == "chat message send" && strings.Contains(message, "--file-path must be a readable local file"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"--file-path 指向的文件不可读",
|
||||
"指定路径不存在、不是普通文件或当前进程没有读取权限,因此无法上传并发送",
|
||||
[]string{"检查路径拼写并确认文件存在", "改用当前用户可读取的绝对路径或工作目录相对路径"},
|
||||
[]string{`dws chat message send --group <openConversationId> --msg-type file --file-path ./report.pdf --format json`},
|
||||
}
|
||||
case path == "chat message send" && strings.Contains(message, "unsupported --msg-type"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"不支持指定的 --msg-type",
|
||||
"card 不是当前命令支持的消息类型;文本或 Markdown 消息无需传 --msg-type",
|
||||
[]string{"文本消息移除 --msg-type 并使用 --text", "媒体消息仅使用 image、file、audio、video、location 或 profile"},
|
||||
[]string{`dws chat message send --group <openConversationId> --text "消息正文" --format json`},
|
||||
}
|
||||
case path == "chat message send" && strings.Contains(message, "message content required"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"群消息缺少正文内容",
|
||||
"未提供 --text 或位置参数,同时也没有选择需要专用参数的媒体消息类型",
|
||||
[]string{"发送文字时补充 --text", "发送文件时使用 --msg-type file --file-path"},
|
||||
[]string{`dws chat message send --group <openConversationId> --text "消息正文" --format json`},
|
||||
}
|
||||
}
|
||||
if guide.message == "" {
|
||||
if required, ok := chatRequiredGuidance[path]; ok &&
|
||||
(strings.Contains(message, "required") || strings.Contains(message, "缺少")) {
|
||||
guide = required
|
||||
} else if strings.HasPrefix(path, "chat ") &&
|
||||
(strings.Contains(message, "required") ||
|
||||
strings.Contains(message, "invalid") ||
|
||||
strings.Contains(message, "unsupported") ||
|
||||
strings.Contains(message, "unknown flag") ||
|
||||
strings.Contains(message, "must be")) {
|
||||
example := fmt.Sprintf("dws %s --help", path)
|
||||
if meta, ok := cli.ResolveMeta(path); ok && len(meta.Selection.Examples) > 0 {
|
||||
example = meta.Selection.Examples[0]
|
||||
if !strings.Contains(example, "--format") {
|
||||
example += " --format json"
|
||||
}
|
||||
}
|
||||
guide = chatWorkbookGuidance{
|
||||
"Chat 命令参数校验失败",
|
||||
message,
|
||||
[]string{"根据错误补齐或修正参数", fmt.Sprintf("运行 dws %s --help 核对当前命令参数", path)},
|
||||
[]string{example},
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return apperrors.NewValidation(
|
||||
guide.message,
|
||||
apperrors.WithReason(guide.reason),
|
||||
apperrors.WithHint(guide.actions[0]),
|
||||
apperrors.WithActions(guide.actions...),
|
||||
apperrors.WithExamples(guide.examples...),
|
||||
apperrors.WithCause(err),
|
||||
)
|
||||
}
|
||||
|
||||
// newPreParseValidationError keeps pipeline handler identity in internal logs
|
||||
// while exposing only the underlying parameter-domain error to CLI users.
|
||||
func newPreParseValidationError(err error) error {
|
||||
@@ -700,9 +220,6 @@ func flagErrorWithSuggestions(cmd *cobra.Command, err error) error {
|
||||
apperrors.WithAvailableFlags(cmdutil.VisibleFlagNames(cmd)...),
|
||||
)
|
||||
}
|
||||
if enriched := enrichChatWorkbookError(cmd, err); enriched != err {
|
||||
return enriched
|
||||
}
|
||||
|
||||
// Common flag aliases and suggestions
|
||||
suggestions := map[string]string{
|
||||
|
||||
@@ -43,153 +43,12 @@ func configureRootHelp(root *cobra.Command) {
|
||||
if cmd != root {
|
||||
defaultHelpFunc(cmd, args)
|
||||
cli.RenderSafetyAnnotation(cmd)
|
||||
renderChatAgentSelectionHint(cmd)
|
||||
return
|
||||
}
|
||||
renderRootHelp(root)
|
||||
})
|
||||
}
|
||||
|
||||
type chatHelpGuidance struct {
|
||||
reason string
|
||||
action string
|
||||
example string
|
||||
}
|
||||
|
||||
var chatWorkbookHelpGuidance = map[string]chatHelpGuidance{
|
||||
"chat group members": {
|
||||
"群成员列表固定使用 --id 传群 openConversationId,不使用消息命令的 --group。",
|
||||
"先查群 ID,再直接执行 members;不要追加多余的 list 子命令。",
|
||||
`dws chat group members --id <openConversationId> --format json`,
|
||||
},
|
||||
"chat group members add": {
|
||||
"添加群成员固定使用 --id 指定群、--users 指定成员。",
|
||||
"先查询群 ID 和成员 userId/openDingTalkId,再执行添加。",
|
||||
`dws chat group members add --id <openConversationId> --users <userId1>,<userId2> --format json`,
|
||||
},
|
||||
"chat group members remove": {
|
||||
"移除群成员使用 --id 和 --users,且不能移除群主。",
|
||||
"先确认成员和不可逆影响,检查群主身份后再执行。",
|
||||
`dws chat group members remove --id <openConversationId> --users <userId> --format json`,
|
||||
},
|
||||
"chat group members add-bot": {
|
||||
"添加机器人属于群成员管理,群参数沿用 --id,并需要 robot-code。",
|
||||
"确认机器人编码和目标群后执行。",
|
||||
`dws chat group members add-bot --id <openConversationId> --robot-code <robotCode> --format json`,
|
||||
},
|
||||
"chat group members remove-bot": {
|
||||
"移除机器人固定使用 --id 指定群、--bot-id 指定群内机器人。",
|
||||
"先列出群机器人取得 openBotId,再执行移除。",
|
||||
`dws chat group members remove-bot --id <openConversationId> --bot-id <openBotId> --format json`,
|
||||
},
|
||||
"chat group members list-by-ids": {
|
||||
"批量查询成员详情使用 --id + --users,users 为成员标识列表。",
|
||||
"确认目标群和成员 ID 后再查询。",
|
||||
`dws chat group members list-by-ids --id <openConversationId> --users <openDingTalkId1>,<openDingTalkId2> --format json`,
|
||||
},
|
||||
"chat group create": {
|
||||
"建群使用 --users;创建结果中的群 ID 可继续传给 members add 和 rename。",
|
||||
"先准备成员 userId,创建后保存返回的 openConversationId。",
|
||||
`dws chat group create --name "项目群" --users <userId1>,<userId2> --format json`,
|
||||
},
|
||||
"chat group rename": {
|
||||
"群改名只使用 --id + --name,不能使用 --group。",
|
||||
"先通过 chat search 获取 openConversationId。",
|
||||
`dws chat group rename --id <openConversationId> --name "新群名" --format json`,
|
||||
},
|
||||
"chat message list": {
|
||||
"message list 按会话和时间拉取消息,不执行服务端关键词搜索。",
|
||||
"按关键词查找时改用 message search;拉历史时提供会话和 time。",
|
||||
`dws chat message list --group <openConversationId> --time "2026-07-30 23:59:59" --direction older --format json`,
|
||||
},
|
||||
"chat message search": {
|
||||
"关键词审计应使用服务端搜索,并同时提供 query、start、end。",
|
||||
"不要用 message list 拉全量后人工筛选。",
|
||||
`dws chat message search --query "评审" --start "2026-07-01T00:00:00+08:00" --end "2026-07-31T23:59:59+08:00" --format json`,
|
||||
},
|
||||
"chat message search-advanced": {
|
||||
"简单关键词优先 message search;只有组合人员、@、会话等条件时才使用 search-advanced。",
|
||||
"至少提供一个真实搜索条件,分页参数不算搜索条件。",
|
||||
`dws chat message search-advanced --query "评审" --conversation-ids <openConversationId> --format json`,
|
||||
},
|
||||
"chat message list-all": {
|
||||
"list-all 按时间跨会话拉取消息,不执行关键词匹配。",
|
||||
"需要关键词时改用 message search,并始终限制时间范围。",
|
||||
`dws chat message list-all --start "2026-07-01T00:00:00+08:00" --end "2026-07-31T23:59:59+08:00" --format json`,
|
||||
},
|
||||
"chat message list-by-sender": {
|
||||
"list-by-sender 的核心条件是发送者;核心条件是关键词时应使用 message search。",
|
||||
"提供发送者 ID 和开始时间,按 nextCursor 翻页。",
|
||||
`dws chat message list-by-sender --sender-user-id <userId> --start "2026-07-01T00:00:00+08:00" --format json`,
|
||||
},
|
||||
}
|
||||
|
||||
func renderChatWorkbookHelpGuidance(cmd *cobra.Command) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
path := strings.TrimSpace(strings.TrimPrefix(cmd.CommandPath(), cmd.Root().Name()+" "))
|
||||
guide, ok := chatWorkbookHelpGuidance[path]
|
||||
if !ok {
|
||||
meta, metaOK := cli.ResolveMeta(path)
|
||||
if !metaOK || meta.Identity.ProductID != "chat" {
|
||||
return
|
||||
}
|
||||
reason := meta.Selection.AgentSummary
|
||||
if reason == "" {
|
||||
reason = "执行前需要确认该 Chat 命令的适用场景、必填参数和安全边界。"
|
||||
}
|
||||
action := "根据帮助正文补齐必填参数,并在实际执行时增加 --format json。"
|
||||
if len(meta.Selection.UseWhen) > 0 {
|
||||
action = meta.Selection.UseWhen[0]
|
||||
}
|
||||
example := "dws " + path + " --format json"
|
||||
if len(meta.Selection.Examples) > 0 {
|
||||
example = meta.Selection.Examples[0]
|
||||
if !strings.Contains(example, "--format") {
|
||||
example += " --format json"
|
||||
}
|
||||
}
|
||||
guide = chatHelpGuidance{reason: reason, action: action, example: example}
|
||||
}
|
||||
w := cmd.ErrOrStderr()
|
||||
_, _ = fmt.Fprintln(w, "错误信息:当前为执行前 guidance,不是运行失败")
|
||||
_, _ = fmt.Fprintln(w, "原因:"+guide.reason)
|
||||
_, _ = fmt.Fprintln(w, "建议操作:")
|
||||
_, _ = fmt.Fprintln(w, "1. "+guide.action)
|
||||
_, _ = fmt.Fprintln(w, "示例:")
|
||||
_, _ = fmt.Fprintln(w, "1. "+guide.example)
|
||||
}
|
||||
|
||||
// renderChatAgentSelectionHint exposes the reviewed Chat selection contract in
|
||||
// command help without reintroducing a second product-local guidance map.
|
||||
// Selection prose remains authored in schema_hints/selection/chat.json and is
|
||||
// consumed through the repository-wide ResolveMeta API.
|
||||
func renderChatAgentSelectionHint(cmd *cobra.Command) {
|
||||
cliPath := strings.TrimSpace(strings.TrimPrefix(cmd.CommandPath(), cmd.Root().Name()+" "))
|
||||
meta, ok := cli.ResolveMeta(cliPath)
|
||||
if !ok || meta.Identity.ProductID != "chat" {
|
||||
return
|
||||
}
|
||||
selection := meta.Selection
|
||||
|
||||
w := cmd.OutOrStdout()
|
||||
_, _ = fmt.Fprintln(w, "Agent guidance:")
|
||||
if selection.AgentSummary != "" {
|
||||
_, _ = fmt.Fprintf(w, " Outcome: %s\n", selection.AgentSummary)
|
||||
}
|
||||
for _, scenario := range selection.UseWhen {
|
||||
_, _ = fmt.Fprintf(w, " Use when: %s\n", scenario)
|
||||
}
|
||||
for _, scenario := range selection.AvoidWhen {
|
||||
_, _ = fmt.Fprintf(w, " Avoid when: %s\n", scenario)
|
||||
}
|
||||
for _, example := range selection.Examples {
|
||||
_, _ = fmt.Fprintf(w, " Example: %s\n", example)
|
||||
}
|
||||
_, _ = fmt.Fprintln(w, " Output: Agent execution should add --format json.")
|
||||
}
|
||||
|
||||
func renderRootHelp(root *cobra.Command) {
|
||||
services := visibleMCPRootCommands(root)
|
||||
utilities := visibleUtilityRootCommands(root)
|
||||
|
||||
@@ -69,77 +69,6 @@ func TestCalendarEventCreateHelpKeepsRoomsStringMetavar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatAgentGuidanceRendersOnlyOnStdout(t *testing.T) {
|
||||
cmd := NewRootCommand()
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.SetOut(&stdout)
|
||||
cmd.SetErr(&stderr)
|
||||
cmd.SetArgs([]string{"chat", "clear-messages", "--help"})
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("chat clear-messages --help: %v\nstdout:\n%s\nstderr:\n%s", err, stdout.String(), stderr.String())
|
||||
}
|
||||
|
||||
for _, want := range []string{"Agent guidance:", "Outcome:", "Use when:", "Avoid when:", "Example:", "Output:"} {
|
||||
if !strings.Contains(stdout.String(), want) {
|
||||
t.Fatalf("chat help stdout missing %q:\n%s", want, stdout.String())
|
||||
}
|
||||
}
|
||||
if got := strings.TrimSpace(stderr.String()); got != "" {
|
||||
t.Fatalf("chat help wrote guidance or warnings to stderr:\n%s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateAlignmentChatGuidance(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []string
|
||||
wants []string
|
||||
}{
|
||||
{[]string{"chat", "message", "query-send-status", "--help"}, []string{"sendStatus=SUCCESS", "FAILED"}},
|
||||
{[]string{"chat", "+messages-query-send-status", "--help"}, []string{"sendStatus=SUCCESS", "openTaskId"}},
|
||||
{[]string{"chat", "message", "set-pin-msg", "--help"}, []string{"openTaskId", "set-top-msg", "chat set-top"}},
|
||||
{[]string{"chat", "message", "unset-pin-msg", "--help"}, []string{"复用", "unset-top-msg"}},
|
||||
{[]string{"chat", "message", "add-emoji", "--help"}, []string{"同一条真实消息", "openTaskId"}},
|
||||
{[]string{"chat", "message", "remove-emoji", "--help"}, []string{"复用", "表情名称"}},
|
||||
{[]string{"chat", "group", "members", "remove", "--help"}, []string{"群主", "转让群主"}},
|
||||
{[]string{"chat", "group", "update-icon", "--help"}, []string{"dentryId", "能力边界"}},
|
||||
{[]string{"chat", "group", "update-settings", "--help"}, []string{"群级设置", "user-settings set"}},
|
||||
{[]string{"chat", "group", "user-settings", "query", "--help"}, []string{"当前用户视角", "保存原值"}},
|
||||
{[]string{"chat", "group", "user-settings", "set", "--help"}, []string{"再次 query", "真实值恢复"}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
cmd := NewRootCommand()
|
||||
var stdout bytes.Buffer
|
||||
cmd.SetOut(&stdout)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("%v: %v", tc.args, err)
|
||||
}
|
||||
for _, want := range tc.wants {
|
||||
if !strings.Contains(stdout.String(), want) {
|
||||
t.Fatalf("%v help missing %q:\n%s", tc.args, want, stdout.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateAlignmentDriveUploadSchemaGuidance(t *testing.T) {
|
||||
cmd := NewRootCommand()
|
||||
var stdout bytes.Buffer
|
||||
cmd.SetOut(&stdout)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"schema", "drive.upload", "--format", "json"})
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("drive.upload schema: %v", err)
|
||||
}
|
||||
for _, want := range []string{"暂时不要发送", "不会发送聊天消息"} {
|
||||
if !strings.Contains(stdout.String(), want) {
|
||||
t.Fatalf("drive.upload schema missing %q:\n%s", want, stdout.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootKeepsMainBranchChatCompatibilityCommands(t *testing.T) {
|
||||
root := NewRootCommand()
|
||||
listDirect := mustFindCommand(t, root, "chat", "message", "list-direct")
|
||||
|
||||
@@ -742,7 +742,7 @@ func (r *runtimeRunner) executeInvocation(ctx context.Context, endpoint string,
|
||||
apperrors.WithOperation("tools/call"),
|
||||
apperrors.WithReason("mcp_tool_error"),
|
||||
apperrors.WithServerKey(invocation.CanonicalProduct),
|
||||
apperrors.WithHint(apperrors.SuggestBusinessHint(callResult.Content)),
|
||||
apperrors.WithHint("MCP tool returned a business error; check tool parameters and refer to skill documentation."),
|
||||
apperrors.WithServerDiag(diag),
|
||||
)
|
||||
// PAT scope error in business response: offer human-readable output and retry
|
||||
@@ -767,7 +767,7 @@ func (r *runtimeRunner) executeInvocation(ctx context.Context, endpoint string,
|
||||
apperrors.WithOperation("tools/call"),
|
||||
apperrors.WithReason("business_error"),
|
||||
apperrors.WithServerKey(invocation.CanonicalProduct),
|
||||
apperrors.WithHint(apperrors.SuggestBusinessHint(callResult.Content)),
|
||||
apperrors.WithHint("The API returned a business-level error. Check required parameters and values."),
|
||||
apperrors.WithServerDiag(diag),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,12 @@ var (
|
||||
manualAgentExamplePlaceholderPattern = regexp.MustCompile(`<([^>]+)>`)
|
||||
manualAgentExampleDryRunJSONPattern = regexp.MustCompile(`(?i)"dry_run"\s*:\s*true`)
|
||||
manualAgentExampleDryRunPlanPattern = regexp.MustCompile(`(?i)"preview_kind"\s*:\s*"plan"`)
|
||||
// manualAgentExampleDryRunInvocationPattern recognizes the executor
|
||||
// envelope (and the dev connect preview, which mirrors its shape): the
|
||||
// top-level contract is an invocation that merely embeds the would-be
|
||||
// request inside response, so it must classify as invocation, not request.
|
||||
manualAgentExampleDryRunInvocationPattern = regexp.MustCompile(
|
||||
`"kind"\s*:\s*"(?:(?:helper|compat|workflow)_invocation|connect_preview)"`)
|
||||
)
|
||||
|
||||
// TestManualAgentExamplesContract is the always-on gate. It validates every
|
||||
@@ -395,6 +401,9 @@ func manualAgentExampleDryRunEvidence(capture manualAgentExampleCapture) (string
|
||||
if manualAgentExampleDryRunJSONPattern.MatchString(capture.Output) && manualAgentExampleDryRunPlanPattern.MatchString(capture.Output) {
|
||||
return cli.DryRunPreviewPlan, true
|
||||
}
|
||||
if manualAgentExampleDryRunJSONPattern.MatchString(capture.Output) && manualAgentExampleDryRunInvocationPattern.MatchString(capture.Output) {
|
||||
return cli.DryRunPreviewInvocation, true
|
||||
}
|
||||
if manualAgentExampleDryRunJSONPattern.MatchString(capture.Output) {
|
||||
return cli.DryRunPreviewRequest, true
|
||||
}
|
||||
@@ -448,6 +457,37 @@ func TestCrossPlatformCoverageManualAgentExampleDryRunEvidenceClassifiesStructur
|
||||
}
|
||||
}
|
||||
|
||||
func TestManualAgentExampleDryRunEvidenceClassifiesExecutorEnvelopeAsInvocation(t *testing.T) {
|
||||
// Executor dry-run envelope: invocation at top level, the would-be request
|
||||
// nested inside response — must classify as invocation, never request.
|
||||
envelope := `{
|
||||
"invocation": {"kind": "helper_invocation", "stage": "helper_override", "implemented": false,
|
||||
"dry_run": true, "canonical_product": "devapp", "tool": "create_dev_app"},
|
||||
"response": {"dry_run": true, "request": {"jsonrpc": "2.0", "method": "tools/call"},
|
||||
"note": "execution skipped by --dry-run"}
|
||||
}`
|
||||
kind, observed := manualAgentExampleDryRunEvidence(manualAgentExampleCapture{Output: envelope})
|
||||
if !observed || kind != cli.DryRunPreviewInvocation {
|
||||
t.Fatalf("executor envelope classified as kind=%q observed=%v, want invocation", kind, observed)
|
||||
}
|
||||
for _, invKind := range []string{"compat_invocation", "workflow_invocation", "connect_preview"} {
|
||||
out := strings.Replace(envelope, "helper_invocation", invKind, 1)
|
||||
if kind, ok := manualAgentExampleDryRunEvidence(manualAgentExampleCapture{Output: out}); !ok || kind != cli.DryRunPreviewInvocation {
|
||||
t.Fatalf("kind %q envelope classified as %q/%v, want invocation", invKind, kind, ok)
|
||||
}
|
||||
}
|
||||
// A bare MCP dry-run document has no invocation kind and stays request.
|
||||
mcpDoc := `{"dry_run": true, "executed": false, "tool": "get_dev_app", "arguments": {}}`
|
||||
if kind, ok := manualAgentExampleDryRunEvidence(manualAgentExampleCapture{Output: mcpDoc}); !ok || kind != cli.DryRunPreviewRequest {
|
||||
t.Fatalf("bare MCP dry-run classified as %q/%v, want request", kind, ok)
|
||||
}
|
||||
// Plan still wins over an invocation-shaped payload.
|
||||
planDoc := `{"dry_run": true, "preview_kind": "plan", "kind": "helper_invocation"}`
|
||||
if kind, ok := manualAgentExampleDryRunEvidence(manualAgentExampleCapture{Output: planDoc}); !ok || kind != cli.DryRunPreviewPlan {
|
||||
t.Fatalf("plan precedence broken: %q/%v", kind, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func manualAgentExamplePromptObserved(output string) bool {
|
||||
normalized := strings.ToLower(output)
|
||||
for _, marker := range []string{
|
||||
|
||||
@@ -458,14 +458,14 @@ func TestPromptingWritesRequireUserConfirmation(t *testing.T) {
|
||||
"sheet.delete_pivot_table": "medium",
|
||||
}
|
||||
wantSources := map[string]string{
|
||||
"attendance.class_create": "internal/cli/schema_hints/metadata/attendance.json",
|
||||
"attendance.class_update": "internal/cli/schema_hints/metadata/attendance.json",
|
||||
"doc.delete_comment": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"doc.version_revert": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"drive.publish_set": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"drive.publish_unset": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"sheet.chart_delete": "internal/cli/schema_hints/metadata/sheet.json",
|
||||
"sheet.delete_pivot_table": "internal/cli/schema_hints/metadata/sheet.json",
|
||||
"attendance.class_create": "corecmd.contract",
|
||||
"attendance.class_update": "corecmd.contract",
|
||||
"doc.delete_comment": "corecmd.contract",
|
||||
"doc.version_revert": "corecmd.contract",
|
||||
"drive.publish_set": "corecmd.contract",
|
||||
"drive.publish_unset": "corecmd.contract",
|
||||
"sheet.chart_delete": "corecmd.contract",
|
||||
"sheet.delete_pivot_table": "corecmd.contract",
|
||||
}
|
||||
canonicals := make([]string, 0, len(wantEffects))
|
||||
for canonical := range wantEffects {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -119,11 +118,11 @@ func TestReviewedRoutedInterfacesReachFinalSchema(t *testing.T) {
|
||||
t.Errorf("missing %s provenance", field)
|
||||
continue
|
||||
}
|
||||
if got := schemaContractString(entry["precedence"]); got != "reviewed_explicit" {
|
||||
t.Errorf("%s provenance precedence = %q, want reviewed_explicit", field, got)
|
||||
if got := schemaContractString(entry["precedence"]); got != "contract_final" {
|
||||
t.Errorf("%s provenance precedence = %q, want contract_final", field, got)
|
||||
}
|
||||
if got := schemaContractString(entry["source"]); !strings.HasSuffix(got, test.sourceSuffix) {
|
||||
t.Errorf("%s provenance source = %q, want suffix %q", field, got, test.sourceSuffix)
|
||||
if got := schemaContractString(entry["source"]); got != "corecmd.contract" {
|
||||
t.Errorf("%s provenance source = %q, want corecmd.contract", field, got)
|
||||
}
|
||||
}
|
||||
if got := provenance["interface_ref"]["value"]; got != nil {
|
||||
@@ -174,11 +173,11 @@ func TestViewGetWrappersUsePinnedGetViewsInterface(t *testing.T) {
|
||||
provenance := schemaContractMap(tool["field_provenance"])
|
||||
for _, field := range []string{"interface_mode", "availability", "interface_ref"} {
|
||||
entry := provenance[field]
|
||||
if got := schemaContractString(entry["precedence"]); got != "reviewed_explicit" {
|
||||
t.Errorf("%s %s precedence = %q, want reviewed_explicit", canonical, field, got)
|
||||
if got := schemaContractString(entry["precedence"]); got != "contract_final" {
|
||||
t.Errorf("%s %s precedence = %q, want contract_final", canonical, field, got)
|
||||
}
|
||||
if got := schemaContractString(entry["source"]); !strings.Contains(got, "internal/cli/schema_hints/metadata/") {
|
||||
t.Errorf("%s %s source = %q, want reviewed interface disposition source", canonical, field, got)
|
||||
if got := schemaContractString(entry["source"]); got != "corecmd.contract" {
|
||||
t.Errorf("%s %s source = %q, want corecmd.contract", canonical, field, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,29 +201,10 @@ func TestReviewedInterfaceDispositionSourceOwnsRuntimeSurface(t *testing.T) {
|
||||
return value
|
||||
}
|
||||
runtimeSurface := load("../cli/schema_hints/runtime-surface-completeness.json")
|
||||
legacyDispositionKeys := load("../cli/schema_hints/zz-interface-disposition-review.json").Tools
|
||||
dispositions := hintFile{Source: map[string]any{"reviewed": true}, Tools: map[string]map[string]any{}}
|
||||
for _, product := range []string{
|
||||
"attendance", "aitable", "chat", "drive", "event", "sheet", "wiki", "doc", "mail", "todo", "calendar", "conference", "contact", "dev", "devdoc", "ding", "live", "minutes", "oa", "pat", "report", "aisearch",
|
||||
} {
|
||||
path := "../cli/schema_hints/metadata/" + product + ".json"
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
continue
|
||||
}
|
||||
file := load(path)
|
||||
for canonical, hint := range file.Tools {
|
||||
if _, ok := legacyDispositionKeys[canonical]; !ok {
|
||||
continue
|
||||
}
|
||||
trimmed := map[string]any{}
|
||||
for _, field := range []string{"interface_mode", "availability", "interface_ref", "interface_reason"} {
|
||||
if value, exists := hint[field]; exists {
|
||||
trimmed[field] = value
|
||||
}
|
||||
}
|
||||
dispositions.Tools[canonical] = trimmed
|
||||
}
|
||||
}
|
||||
// After hints→declarations migration, disposition facts live in compiled
|
||||
// ContractFinal (corecmd.contract). zz-interface-disposition-review.json
|
||||
// remains the audit key set + expected values for this gate.
|
||||
dispositions := load("../cli/schema_hints/zz-interface-disposition-review.json")
|
||||
if dispositions.Source["reviewed"] != true {
|
||||
t.Fatalf("interface disposition source reviewed = %#v, want true", dispositions.Source["reviewed"])
|
||||
}
|
||||
@@ -314,11 +294,11 @@ func TestReviewedInterfaceDispositionSourceOwnsRuntimeSurface(t *testing.T) {
|
||||
}
|
||||
for _, field := range fields {
|
||||
entry := provenance[field]
|
||||
if got := schemaContractString(entry["precedence"]); got != "reviewed_explicit" {
|
||||
t.Errorf("%s final %s precedence = %q, want reviewed_explicit", canonical, field, got)
|
||||
if got := schemaContractString(entry["precedence"]); got != "contract_final" {
|
||||
t.Errorf("%s final %s precedence = %q, want contract_final", canonical, field, got)
|
||||
}
|
||||
if got := schemaContractString(entry["source"]); !strings.Contains(got, "internal/cli/schema_hints/metadata/") {
|
||||
t.Errorf("%s final %s source = %q, want reviewed disposition source", canonical, field, got)
|
||||
if got := schemaContractString(entry["source"]); got != "corecmd.contract" {
|
||||
t.Errorf("%s final %s source = %q, want corecmd.contract", canonical, field, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,45 +15,53 @@ type finalSchemaSafetyWant struct {
|
||||
risk string
|
||||
confirmation string
|
||||
idempotency string
|
||||
// provenance is the expected field_provenance precedence; empty defaults
|
||||
// to "reviewed_explicit" (hints path). Contract-declared tools (devapp)
|
||||
// deliver "contract_final".
|
||||
provenance string
|
||||
}
|
||||
|
||||
func TestReviewedMutationSafetyReachesFinalSchema(t *testing.T) {
|
||||
const declared = "contract_final"
|
||||
wants := []finalSchemaSafetyWant{
|
||||
{canonical: "aitable.form_field_hide", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "idempotent"},
|
||||
{canonical: "chat.dismiss_group", effect: "destructive", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "drive.recycle_restore", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown"},
|
||||
{canonical: "minutes.create_speaker_summary", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown"},
|
||||
{canonical: "sheet.clear_range", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "sheet.batch_update", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "sheet.range_batch_clear", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "sheet.group_dimension", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown"},
|
||||
{canonical: "sheet.sort_filter", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown"},
|
||||
{canonical: "sheet.ungroup_dimension", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown"},
|
||||
{canonical: "aitable.form_field_hide", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "idempotent", provenance: declared},
|
||||
{canonical: "chat.dismiss_group", effect: "destructive", risk: "high", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "drive.recycle_restore", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "minutes.create_speaker_summary", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "sheet.clear_range", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "sheet.batch_update", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "sheet.range_batch_clear", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "sheet.group_dimension", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "sheet.sort_filter", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "sheet.ungroup_dimension", effect: "write", risk: "medium", confirmation: "not_required", idempotency: "unknown", provenance: declared},
|
||||
}
|
||||
assertFinalSchemaSafety(t, wants)
|
||||
}
|
||||
|
||||
func TestDevAppWriteGuardRequiresFinalSchemaConfirmation(t *testing.T) {
|
||||
// devapp 全树已声明化:provenance 为 contract_final;风险按可逆性分级
|
||||
// (可逆写 medium / 难撤回 high-write / 删除与发布 destructive)。
|
||||
const declared = "contract_final"
|
||||
wants := []finalSchemaSafetyWant{
|
||||
{canonical: "dev.add_dev_app_members", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.apply_dev_app_permissions", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.create_dev_app", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.create_dev_app_version", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.delete_dev_app", effect: "destructive", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.disable_dev_app", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.disable_dev_app_robot", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.enable_dev_app", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.enable_dev_app_robot", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.publish_dev_app_version", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.remove_dev_app_members", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.remove_dev_app_permissions", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.set_extension_robot_config", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.set_extension_webapp_config", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.submit_robot_create_task", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.subscribe_dev_app_events", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.unsubscribe_dev_app_events", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.update_dev_app", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.update_dev_app_security_config", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown"},
|
||||
{canonical: "dev.add_dev_app_members", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.apply_dev_app_permissions", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.create_dev_app", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.create_dev_app_version", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.delete_dev_app", effect: "destructive", risk: "high", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.disable_dev_app", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.disable_dev_app_robot", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.enable_dev_app", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.enable_dev_app_robot", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.publish_dev_app_version", effect: "destructive", risk: "high", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.remove_dev_app_members", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.remove_dev_app_permissions", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.set_extension_robot_config", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.set_extension_webapp_config", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.submit_robot_create_task", effect: "write", risk: "high", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.subscribe_dev_app_events", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.unsubscribe_dev_app_events", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.update_dev_app", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
{canonical: "dev.update_dev_app_security_config", effect: "write", risk: "medium", confirmation: "user_required", idempotency: "unknown", provenance: declared},
|
||||
}
|
||||
assertFinalSchemaSafety(t, wants)
|
||||
}
|
||||
@@ -77,13 +85,17 @@ func assertFinalSchemaSafety(t *testing.T, wants []finalSchemaSafetyWant) {
|
||||
"confirmation": want.confirmation,
|
||||
"idempotency": want.idempotency,
|
||||
}
|
||||
wantProvenance := want.provenance
|
||||
if wantProvenance == "" {
|
||||
wantProvenance = "reviewed_explicit"
|
||||
}
|
||||
provenance := schemaContractMap(tool["field_provenance"])
|
||||
for field, expected := range values {
|
||||
if got := schemaContractString(tool[field]); got != expected {
|
||||
t.Errorf("%s = %q, want %q", field, got, expected)
|
||||
}
|
||||
if got := schemaContractString(provenance[field]["precedence"]); got != "reviewed_explicit" {
|
||||
t.Errorf("%s provenance precedence = %q, want reviewed_explicit", field, got)
|
||||
if got := schemaContractString(provenance[field]["precedence"]); got != wantProvenance {
|
||||
t.Errorf("%s provenance precedence = %q, want %s", field, got, wantProvenance)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -106,7 +106,7 @@ func TestEmbeddedShortcutProgressiveQueriesReturnCompleteContracts(t *testing.T)
|
||||
|
||||
product := executeShortcutSchemaQuery(t, "chat")
|
||||
productPayload, _ := product["product"].(map[string]any)
|
||||
if got, want := int(product["count"].(float64)), 159; got != want {
|
||||
if got, want := int(product["count"].(float64)), 129; got != want {
|
||||
t.Fatalf("schema chat count = %d, want %d", got, want)
|
||||
}
|
||||
summaries := schemaContractObjectSlice(productPayload["tools"])
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ContractFinalPayload is the Contract-authored final Schema leaf overlay.
|
||||
// Registered in-process by the framework; Schema assembly reads it as
|
||||
// pass-through. No JSON bridge. Treat as read-only after Register.
|
||||
type ContractFinalPayload struct {
|
||||
Title string
|
||||
Description string
|
||||
Positionals []RuntimeSchemaPositional
|
||||
Safety *SafetySpec
|
||||
DryRun *DryRunSpec
|
||||
Interface *InterfaceSpec
|
||||
Selection *SelectionSpec
|
||||
Identity *ToolIdentitySpec
|
||||
}
|
||||
|
||||
var contractFinalByCommand sync.Map // *cobra.Command → *ContractFinalPayload
|
||||
|
||||
// RegisterRuntimeContractFinal stores the typed final Schema overlay for a leaf.
|
||||
// Light runtime write: one map store, no JSON, no deep clone.
|
||||
func RegisterRuntimeContractFinal(cmd *cobra.Command, payload ContractFinalPayload) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
AnnotateRuntimeContract(cmd)
|
||||
p := payload
|
||||
contractFinalByCommand.Store(cmd, &p)
|
||||
}
|
||||
|
||||
// RuntimeContractFinal returns the registered final Schema overlay (read-only).
|
||||
func RuntimeContractFinal(cmd *cobra.Command) (ContractFinalPayload, bool) {
|
||||
if cmd == nil {
|
||||
return ContractFinalPayload{}, false
|
||||
}
|
||||
raw, ok := contractFinalByCommand.Load(cmd)
|
||||
if !ok {
|
||||
return ContractFinalPayload{}, false
|
||||
}
|
||||
p, ok := raw.(*ContractFinalPayload)
|
||||
if !ok || p == nil {
|
||||
return ContractFinalPayload{}, false
|
||||
}
|
||||
return *p, true
|
||||
}
|
||||
|
||||
// HasRuntimeContractFinal reports whether the leaf has a registered final overlay.
|
||||
func HasRuntimeContractFinal(cmd *cobra.Command) bool {
|
||||
if cmd == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := contractFinalByCommand.Load(cmd)
|
||||
return ok
|
||||
}
|
||||
|
||||
// ClearRuntimeContractFinalForTest removes a registration (tests only).
|
||||
func ClearRuntimeContractFinalForTest(cmd *cobra.Command) {
|
||||
if cmd != nil {
|
||||
contractFinalByCommand.Delete(cmd)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/app"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||||
)
|
||||
|
||||
// TestAllCommandsContractFinalConsistentWithLiveAndEmbeddedCatalog walks every
|
||||
// reviewed registry leaf and checks, one by one:
|
||||
// 1. PrimaryCommand has runtime ContractFinal (command-side declaration)
|
||||
// 2. Live assembly ToolSpec fields match that ContractFinal
|
||||
// 3. Embedded full-leaf shard fields match live ToolSpec (incl. examples)
|
||||
func TestAllCommandsContractFinalConsistentWithLiveAndEmbeddedCatalog(t *testing.T) {
|
||||
root := app.NewSchemaSourceRootCommand()
|
||||
liveReg, err := cli.AssembleSchemaRegistry(root)
|
||||
if err != nil {
|
||||
t.Fatalf("AssembleSchemaRegistry(live) error = %v", err)
|
||||
}
|
||||
embedByCanon, err := loadEmbeddedFullLeafTools()
|
||||
if err != nil {
|
||||
t.Fatalf("load embedded full leaf shards: %v", err)
|
||||
}
|
||||
|
||||
effective, err := cli.BuildEffectiveCommandRegistry(root)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildEffectiveCommandRegistry() error = %v", err)
|
||||
}
|
||||
bound, err := cli.BindEffectiveCommandRegistry(root, effective)
|
||||
if err != nil {
|
||||
t.Fatalf("BindEffectiveCommandRegistry() error = %v", err)
|
||||
}
|
||||
|
||||
liveByCanon := indexLiveTools(liveReg)
|
||||
|
||||
type row struct {
|
||||
canonical string
|
||||
cliPath string
|
||||
status string
|
||||
detail string
|
||||
}
|
||||
rows := make([]row, 0, len(bound.Commands))
|
||||
var failCount int
|
||||
checked := 0
|
||||
|
||||
canonOrder := make([]string, 0, len(bound.Commands))
|
||||
boundByCanon := make(map[string]cli.BoundCommandSpec, len(bound.Commands))
|
||||
for _, cmd := range bound.Commands {
|
||||
if cmd.CanonicalPath == "" {
|
||||
continue
|
||||
}
|
||||
canonOrder = append(canonOrder, cmd.CanonicalPath)
|
||||
boundByCanon[cmd.CanonicalPath] = cmd
|
||||
}
|
||||
sort.Strings(canonOrder)
|
||||
|
||||
for _, canonical := range canonOrder {
|
||||
cmd := boundByCanon[canonical]
|
||||
cliPath := cmd.PrimaryCLIPath
|
||||
liveTool, ok := liveByCanon[canonical]
|
||||
if !ok {
|
||||
failCount++
|
||||
rows = append(rows, row{canonical, cliPath, "FAIL", "absent from live registry"})
|
||||
continue
|
||||
}
|
||||
embedTool, ok := embedByCanon[canonical]
|
||||
if !ok {
|
||||
failCount++
|
||||
rows = append(rows, row{canonical, cliPath, "FAIL", "absent from embedded full leaf shard"})
|
||||
continue
|
||||
}
|
||||
final, has := cli.RuntimeContractFinal(cmd.PrimaryCommand)
|
||||
if !has {
|
||||
failCount++
|
||||
rows = append(rows, row{canonical, cliPath, "FAIL", "no RuntimeContractFinal on PrimaryCommand"})
|
||||
continue
|
||||
}
|
||||
checked++
|
||||
problems := compareFinalToLive(canonical, final, liveTool)
|
||||
problems = append(problems, compareLiveToEmbedded(canonical, liveTool, embedTool)...)
|
||||
if len(problems) > 0 {
|
||||
failCount++
|
||||
rows = append(rows, row{canonical, cliPath, "FAIL", strings.Join(problems, "; ")})
|
||||
continue
|
||||
}
|
||||
if liveTool.MetadataSource != "corecmd.contract" {
|
||||
failCount++
|
||||
rows = append(rows, row{canonical, cliPath, "FAIL", "metadata_source=" + liveTool.MetadataSource})
|
||||
continue
|
||||
}
|
||||
rows = append(rows, row{canonical, cliPath, "OK", "ContractFinal=live=catalog"})
|
||||
}
|
||||
|
||||
reportPath := filepath.Join(t.TempDir(), "contract_final_per_command.txt")
|
||||
var b strings.Builder
|
||||
for i, r := range rows {
|
||||
line := fmt.Sprintf("%04d %s %-48s %-60s %s\n", i+1, r.status, r.canonical, r.cliPath, r.detail)
|
||||
b.WriteString(line)
|
||||
if r.status != "OK" {
|
||||
t.Errorf("%s", strings.TrimSpace(line))
|
||||
}
|
||||
}
|
||||
summary := fmt.Sprintf("SUMMARY checked=%d fail=%d ok=%d live=%d embed=%d\n",
|
||||
checked, failCount, checked-failCount, len(liveByCanon), len(embedByCanon))
|
||||
b.WriteString(summary)
|
||||
if err := os.WriteFile(reportPath, []byte(b.String()), 0o644); err != nil {
|
||||
t.Fatalf("write report: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("per-command report: %s", reportPath)
|
||||
t.Logf("%s", strings.TrimSpace(summary))
|
||||
// Print every line so -v shows 逐个检查.
|
||||
for _, r := range rows {
|
||||
t.Logf("%s %-48s %s", r.status, r.canonical, r.cliPath)
|
||||
}
|
||||
|
||||
if failCount > 0 {
|
||||
t.Fatalf("per-command consistency failed: %d/%d", failCount, len(rows))
|
||||
}
|
||||
if checked != 845 {
|
||||
t.Fatalf("checked = %d, want 845 reviewed tools", checked)
|
||||
}
|
||||
if err := cli.ValidateEmbeddedRuntimeSchemaCompleteness(app.NewSchemaSourceRootCommand()); err != nil {
|
||||
t.Fatalf("ValidateEmbeddedRuntimeSchemaCompleteness() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type liveToolView struct {
|
||||
Description string
|
||||
MetadataSource string
|
||||
Effect string
|
||||
Risk string
|
||||
Confirmation string
|
||||
Idempotency string
|
||||
InterfaceMode string
|
||||
Availability string
|
||||
InterfacePID string
|
||||
InterfaceRPC string
|
||||
InterfaceReason string
|
||||
AgentSummary string
|
||||
UseWhen []string
|
||||
AvoidWhen []string
|
||||
Examples []string
|
||||
}
|
||||
|
||||
type embedToolView struct {
|
||||
Description string
|
||||
MetadataSource string
|
||||
Effect string
|
||||
Risk string
|
||||
Confirmation string
|
||||
Idempotency string
|
||||
InterfaceMode string
|
||||
Availability string
|
||||
InterfacePID string
|
||||
InterfaceRPC string
|
||||
AgentSummary string
|
||||
UseWhen []string
|
||||
AvoidWhen []string
|
||||
Examples []string
|
||||
}
|
||||
|
||||
func indexLiveTools(reg cli.SchemaRegistry) map[string]liveToolView {
|
||||
out := make(map[string]liveToolView)
|
||||
for _, product := range reg.Products {
|
||||
for _, tool := range product.Tools {
|
||||
view := liveToolView{
|
||||
Description: tool.Description,
|
||||
MetadataSource: tool.MetadataSource,
|
||||
Effect: tool.Safety.Effect,
|
||||
Risk: tool.Safety.Risk,
|
||||
Confirmation: tool.Safety.Confirmation,
|
||||
Idempotency: tool.Safety.Idempotency,
|
||||
InterfaceMode: tool.Interface.Mode,
|
||||
Availability: tool.Interface.Availability,
|
||||
InterfaceReason: tool.Interface.Reason,
|
||||
AgentSummary: tool.Selection.AgentSummary,
|
||||
UseWhen: append([]string(nil), tool.Selection.UseWhen...),
|
||||
AvoidWhen: append([]string(nil), tool.Selection.AvoidWhen...),
|
||||
Examples: append([]string(nil), tool.Selection.Examples...),
|
||||
}
|
||||
if tool.Interface.Ref != nil {
|
||||
view.InterfacePID = tool.Interface.Ref.ProductID
|
||||
view.InterfaceRPC = tool.Interface.Ref.RPCName
|
||||
}
|
||||
out[tool.Identity.CanonicalPath] = view
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func repoCLIDir() string {
|
||||
_, file, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
return "."
|
||||
}
|
||||
return filepath.Dir(file)
|
||||
}
|
||||
|
||||
func loadEmbeddedFullLeafTools() (map[string]embedToolView, error) {
|
||||
toolsDir := filepath.Join(repoCLIDir(), "schema_catalog", "tools")
|
||||
entries, err := os.ReadDir(toolsDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[string]embedToolView)
|
||||
for _, ent := range entries {
|
||||
if ent.IsDir() || !strings.HasSuffix(ent.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(toolsDir, ent.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var shard struct {
|
||||
Tools map[string]map[string]any `json:"tools"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &shard); err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", ent.Name(), err)
|
||||
}
|
||||
for key, raw := range shard.Tools {
|
||||
canonical := asString(raw["canonical_path"])
|
||||
if canonical == "" {
|
||||
canonical = key
|
||||
}
|
||||
view := embedToolView{
|
||||
Description: asString(raw["description"]),
|
||||
MetadataSource: asString(raw["metadata_source"]),
|
||||
Effect: asString(raw["effect"]),
|
||||
Risk: asString(raw["risk"]),
|
||||
Confirmation: asString(raw["confirmation"]),
|
||||
Idempotency: asString(raw["idempotency"]),
|
||||
InterfaceMode: asString(raw["interface_mode"]),
|
||||
Availability: asString(raw["availability"]),
|
||||
AgentSummary: asString(raw["agent_summary"]),
|
||||
UseWhen: asStringSlice(raw["use_when"]),
|
||||
AvoidWhen: asStringSlice(raw["avoid_when"]),
|
||||
Examples: asStringSlice(raw["examples"]),
|
||||
}
|
||||
if ref, ok := raw["interface_ref"].(map[string]any); ok {
|
||||
view.InterfacePID = asString(ref["product_id"])
|
||||
view.InterfaceRPC = asString(ref["rpc_name"])
|
||||
}
|
||||
out[canonical] = view
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func compareFinalToLive(canonical string, final cli.ContractFinalPayload, tool liveToolView) []string {
|
||||
var problems []string
|
||||
add := func(field, want, got string) {
|
||||
if strings.TrimSpace(want) == "" {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(want) != strings.TrimSpace(got) {
|
||||
problems = append(problems, fmt.Sprintf("%s: final=%q live=%q", field, want, got))
|
||||
}
|
||||
}
|
||||
if final.Description != "" {
|
||||
add("description", final.Description, tool.Description)
|
||||
}
|
||||
if final.Safety != nil {
|
||||
add("effect", final.Safety.Effect, tool.Effect)
|
||||
add("risk", final.Safety.Risk, tool.Risk)
|
||||
add("confirmation", final.Safety.Confirmation, tool.Confirmation)
|
||||
add("idempotency", final.Safety.Idempotency, tool.Idempotency)
|
||||
}
|
||||
if final.Interface != nil {
|
||||
add("interface_mode", final.Interface.Mode, tool.InterfaceMode)
|
||||
add("availability", final.Interface.Availability, tool.Availability)
|
||||
add("interface_reason", final.Interface.Reason, tool.InterfaceReason)
|
||||
if final.Interface.Ref != nil {
|
||||
add("interface_product", final.Interface.Ref.ProductID, tool.InterfacePID)
|
||||
add("interface_rpc", final.Interface.Ref.RPCName, tool.InterfaceRPC)
|
||||
}
|
||||
}
|
||||
if final.Selection != nil {
|
||||
add("agent_summary", final.Selection.AgentSummary, tool.AgentSummary)
|
||||
if !reflect.DeepEqual(final.Selection.UseWhen, tool.UseWhen) {
|
||||
problems = append(problems, "use_when mismatch")
|
||||
}
|
||||
if !reflect.DeepEqual(final.Selection.AvoidWhen, tool.AvoidWhen) {
|
||||
problems = append(problems, "avoid_when mismatch")
|
||||
}
|
||||
if !reflect.DeepEqual(final.Selection.Examples, tool.Examples) {
|
||||
problems = append(problems, "examples mismatch")
|
||||
}
|
||||
}
|
||||
_ = canonical
|
||||
return problems
|
||||
}
|
||||
|
||||
func compareLiveToEmbedded(canonical string, live liveToolView, embed embedToolView) []string {
|
||||
var problems []string
|
||||
add := func(field, a, b string) {
|
||||
if strings.TrimSpace(a) != strings.TrimSpace(b) {
|
||||
problems = append(problems, fmt.Sprintf("%s: live=%q embed=%q", field, a, b))
|
||||
}
|
||||
}
|
||||
add("description", live.Description, embed.Description)
|
||||
add("effect", live.Effect, embed.Effect)
|
||||
add("risk", live.Risk, embed.Risk)
|
||||
add("confirmation", live.Confirmation, embed.Confirmation)
|
||||
add("idempotency", live.Idempotency, embed.Idempotency)
|
||||
add("interface_mode", live.InterfaceMode, embed.InterfaceMode)
|
||||
add("availability", live.Availability, embed.Availability)
|
||||
add("agent_summary", live.AgentSummary, embed.AgentSummary)
|
||||
add("metadata_source", live.MetadataSource, embed.MetadataSource)
|
||||
add("interface_product", live.InterfacePID, embed.InterfacePID)
|
||||
add("interface_rpc", live.InterfaceRPC, embed.InterfaceRPC)
|
||||
if !reflect.DeepEqual(live.UseWhen, embed.UseWhen) {
|
||||
problems = append(problems, "use_when live!=embed")
|
||||
}
|
||||
if !reflect.DeepEqual(live.AvoidWhen, embed.AvoidWhen) {
|
||||
problems = append(problems, "avoid_when live!=embed")
|
||||
}
|
||||
if !reflect.DeepEqual(live.Examples, embed.Examples) {
|
||||
problems = append(problems, "examples live!=embed")
|
||||
}
|
||||
_ = canonical
|
||||
return problems
|
||||
}
|
||||
|
||||
func asString(v any) string {
|
||||
s, _ := v.(string)
|
||||
return s
|
||||
}
|
||||
|
||||
func asStringSlice(v any) []string {
|
||||
raw, ok := v.([]any)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(raw))
|
||||
for _, item := range raw {
|
||||
if s, ok := item.(string); ok {
|
||||
out = append(out, s)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func TestContractFinalTypedRegistryNoJSON(t *testing.T) {
|
||||
cmd := &cobra.Command{Use: "x"}
|
||||
t.Cleanup(func() { ClearRuntimeContractFinalForTest(cmd) })
|
||||
|
||||
RegisterRuntimeContractFinal(cmd, ContractFinalPayload{
|
||||
Title: "T",
|
||||
Safety: &SafetySpec{
|
||||
Effect: "write", Confirmation: "user_required", Idempotency: "retryable",
|
||||
},
|
||||
Selection: &SelectionSpec{AgentSummary: "sum", UseWhen: []string{"u"}},
|
||||
Identity: &ToolIdentitySpec{ProductID: "p", Name: "n"},
|
||||
})
|
||||
if cmd.Annotations != nil {
|
||||
if _, ok := cmd.Annotations["dws.schema.final"]; ok {
|
||||
t.Fatal("must not write JSON annotation dws.schema.final")
|
||||
}
|
||||
}
|
||||
got, ok := RuntimeContractFinal(cmd)
|
||||
if !ok || got.Title != "T" || got.Safety == nil || got.Safety.Idempotency != "retryable" {
|
||||
t.Fatalf("got %#v ok=%v", got, ok)
|
||||
}
|
||||
if got.Selection == nil || got.Selection.Reviewed != nil {
|
||||
t.Fatalf("selection must not carry reviewed fields: %#v", got.Selection)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeToolSpecFromContractFinalPassThrough(t *testing.T) {
|
||||
cmd := &cobra.Command{Use: "create", Short: "s", Long: "l"}
|
||||
t.Cleanup(func() { ClearRuntimeContractFinalForTest(cmd) })
|
||||
cmd.Flags().String("mode", "", "usage")
|
||||
AnnotateRuntimeFlag(cmd, "mode", "mode", "string", false, "")
|
||||
RegisterRuntimeContractFinal(cmd, ContractFinalPayload{
|
||||
Title: "Final Title",
|
||||
Safety: &SafetySpec{
|
||||
Effect: "write", Confirmation: "user_required", Idempotency: "none",
|
||||
},
|
||||
DryRun: &DryRunSpec{PreviewKind: DryRunPreviewInvocation},
|
||||
Selection: &SelectionSpec{
|
||||
AgentSummary: "from contract",
|
||||
UseWhen: []string{"create things"},
|
||||
},
|
||||
Identity: &ToolIdentitySpec{
|
||||
ProductID: "dev", Name: "create_thing", CanonicalPath: "dev.create_thing",
|
||||
},
|
||||
})
|
||||
|
||||
entry := runtimeSchemaEntry{
|
||||
ProductID: "dev",
|
||||
ToolName: "create_thing",
|
||||
CLIName: "create",
|
||||
CLIPath: "dev create",
|
||||
PrimaryCLIPath: "dev create",
|
||||
ProductName: "Dev",
|
||||
Command: cmd,
|
||||
Source: "test",
|
||||
}
|
||||
spec, err := runtimeToolSpecFromContractFinal(entry, mustFinal(t, cmd), runtimeSchemaMetadataSources{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if spec.Title != "Final Title" {
|
||||
t.Fatalf("title = %q", spec.Title)
|
||||
}
|
||||
if spec.Safety.Confirmation != "user_required" || spec.Safety.Idempotency != "none" {
|
||||
t.Fatalf("safety = %#v", spec.Safety)
|
||||
}
|
||||
if spec.DryRun == nil || spec.DryRun.PreviewKind != DryRunPreviewInvocation {
|
||||
t.Fatalf("dry_run = %#v", spec.DryRun)
|
||||
}
|
||||
if spec.Selection.AgentSummary != "from contract" {
|
||||
t.Fatalf("selection = %#v", spec.Selection)
|
||||
}
|
||||
if spec.MetadataSource != "corecmd.contract" {
|
||||
t.Fatalf("metadata_source = %q", spec.MetadataSource)
|
||||
}
|
||||
if len(spec.Parameters) != 1 || spec.Parameters[0].Name != "mode" {
|
||||
t.Fatalf("parameters = %#v", spec.Parameters)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeToolSpecFromContractFinalIdentityMismatchFails(t *testing.T) {
|
||||
entry := runtimeSchemaEntry{
|
||||
ProductID: "dev",
|
||||
ToolName: "create_thing",
|
||||
CLIName: "create",
|
||||
CLIPath: "dev create",
|
||||
PrimaryCLIPath: "dev create",
|
||||
Command: &cobra.Command{Use: "create"},
|
||||
Source: "test",
|
||||
}
|
||||
for name, id := range map[string]ToolIdentitySpec{
|
||||
"wrong name": {Name: "delete_thing"},
|
||||
"wrong canonical path": {CanonicalPath: "dev.delete_thing"},
|
||||
"wrong product": {ProductID: "other"},
|
||||
"wrong cli path": {CLIPath: "dev delete"},
|
||||
"wrong aliases": {Aliases: []string{"dev rm"}},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
_, err := runtimeToolSpecFromContractFinal(entry, ContractFinalPayload{Identity: &id}, runtimeSchemaMetadataSources{})
|
||||
if err == nil {
|
||||
t.Fatal("declared identity conflicting with bound entry must fail assembly")
|
||||
}
|
||||
})
|
||||
}
|
||||
consistent := ContractFinalPayload{Identity: &ToolIdentitySpec{
|
||||
ProductID: "dev", Name: "create_thing", CLIName: "create",
|
||||
CanonicalPath: "dev.create_thing", CLIPath: "dev create",
|
||||
PrimaryCLIPath: "dev create", Source: "test",
|
||||
}}
|
||||
if _, err := runtimeToolSpecFromContractFinal(entry, consistent, runtimeSchemaMetadataSources{}); err != nil {
|
||||
t.Fatalf("declared identity matching bound entry must pass: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeToolSpecFromContractFinalRejectsReviewedSelection(t *testing.T) {
|
||||
reviewed := true
|
||||
entry := runtimeSchemaEntry{
|
||||
ProductID: "dev",
|
||||
ToolName: "create_thing",
|
||||
Command: &cobra.Command{Use: "create"},
|
||||
}
|
||||
_, err := runtimeToolSpecFromContractFinal(entry, ContractFinalPayload{
|
||||
Selection: &SelectionSpec{AgentSummary: "sum", Reviewed: &reviewed},
|
||||
}, runtimeSchemaMetadataSources{})
|
||||
if err == nil {
|
||||
t.Fatal("declaration payload carrying Reviewed must fail assembly (reviewed is legacy-path only)")
|
||||
}
|
||||
}
|
||||
|
||||
func mustFinal(t *testing.T, cmd *cobra.Command) ContractFinalPayload {
|
||||
t.Helper()
|
||||
final, ok := RuntimeContractFinal(cmd)
|
||||
if !ok {
|
||||
t.Fatal("missing final")
|
||||
}
|
||||
return final
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/app"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd"
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
||||
)
|
||||
|
||||
// TestUserRequiredSafetyHomologyWithRuntimeGate proves Catalog Safety and the
|
||||
// executable confirmation gate share one source for every live user_required leaf:
|
||||
//
|
||||
// 1. ContractFinal.Safety.Confirmation == live ToolSpec.Confirmation
|
||||
// 2. Runtime gate is installed: DeclareLeafMetadata (HasContractConfirmSafety),
|
||||
// Sheet protect marker, or framework NewCommand/Shortcut RunE (verified by
|
||||
// closed-stdin Execute → confirmation_required / 用户取消了操作 without --yes)
|
||||
func TestUserRequiredSafetyHomologyWithRuntimeGate(t *testing.T) {
|
||||
root := app.NewSchemaSourceRootCommand()
|
||||
if root.PersistentFlags().Lookup("yes") == nil {
|
||||
root.PersistentFlags().Bool("yes", false, "")
|
||||
}
|
||||
if root.PersistentFlags().Lookup("dry-run") == nil {
|
||||
root.PersistentFlags().Bool("dry-run", false, "")
|
||||
}
|
||||
|
||||
liveReg, err := cli.AssembleSchemaRegistry(root)
|
||||
if err != nil {
|
||||
t.Fatalf("AssembleSchemaRegistry: %v", err)
|
||||
}
|
||||
effective, err := cli.BuildEffectiveCommandRegistry(root)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildEffectiveCommandRegistry: %v", err)
|
||||
}
|
||||
bound, err := cli.BindEffectiveCommandRegistry(root, effective)
|
||||
if err != nil {
|
||||
t.Fatalf("BindEffectiveCommandRegistry: %v", err)
|
||||
}
|
||||
|
||||
liveByCanon := make(map[string]string) // canonical → confirmation
|
||||
liveToolByCanon := make(map[string]cli.ToolSpec)
|
||||
for _, product := range liveReg.Products {
|
||||
for _, tool := range product.Tools {
|
||||
liveByCanon[tool.Identity.CanonicalPath] = strings.TrimSpace(tool.Safety.Confirmation)
|
||||
liveToolByCanon[tool.Identity.CanonicalPath] = tool
|
||||
}
|
||||
}
|
||||
|
||||
type row struct {
|
||||
canonical string
|
||||
cliPath string
|
||||
gate string
|
||||
detail string
|
||||
}
|
||||
var fails []row
|
||||
var okRows []row
|
||||
checked := 0
|
||||
|
||||
canonOrder := make([]string, 0, len(bound.Commands))
|
||||
boundByCanon := make(map[string]cli.BoundCommandSpec, len(bound.Commands))
|
||||
for _, cmd := range bound.Commands {
|
||||
if cmd.CanonicalPath == "" {
|
||||
continue
|
||||
}
|
||||
canonOrder = append(canonOrder, cmd.CanonicalPath)
|
||||
boundByCanon[cmd.CanonicalPath] = cmd
|
||||
}
|
||||
sort.Strings(canonOrder)
|
||||
|
||||
for _, canonical := range canonOrder {
|
||||
cmd := boundByCanon[canonical]
|
||||
leaf := cmd.PrimaryCommand
|
||||
if leaf == nil {
|
||||
fails = append(fails, row{canonical, cmd.PrimaryCLIPath, "", "nil PrimaryCommand"})
|
||||
continue
|
||||
}
|
||||
final, hasFinal := cli.RuntimeContractFinal(leaf)
|
||||
if !hasFinal || final.Safety == nil {
|
||||
continue
|
||||
}
|
||||
conf := strings.TrimSpace(final.Safety.Confirmation)
|
||||
if conf != "user_required" {
|
||||
continue
|
||||
}
|
||||
checked++
|
||||
cliPath := cmd.PrimaryCLIPath
|
||||
|
||||
liveConf, ok := liveByCanon[canonical]
|
||||
if !ok {
|
||||
fails = append(fails, row{canonical, cliPath, "", "absent from live registry"})
|
||||
continue
|
||||
}
|
||||
if liveConf != "user_required" {
|
||||
fails = append(fails, row{canonical, cliPath, "", fmt.Sprintf(
|
||||
"ContractFinal=user_required but live ToolSpec.Confirmation=%q", liveConf)})
|
||||
continue
|
||||
}
|
||||
|
||||
gate := ""
|
||||
switch {
|
||||
case helpers.HasContractConfirmSafety(leaf) && helpers.HasSheetMutationConfirmationGuard(leaf):
|
||||
gate = "declare_leaf+sheet_marker"
|
||||
case helpers.HasContractConfirmSafety(leaf):
|
||||
gate = "declare_leaf_confirm"
|
||||
case helpers.HasSheetMutationConfirmationGuard(leaf):
|
||||
gate = "sheet_protect"
|
||||
default:
|
||||
// NewLeafCommand / Shortcut: NewCommand registers ContractFinal from the
|
||||
// same SafetySpec it wires into ConfirmSafety. Prove that SafetySpec is
|
||||
// still user_required-operable (closed stdin → confirmation gate).
|
||||
leaf.SetIn(strings.NewReader(""))
|
||||
leaf.SetOut(io.Discard)
|
||||
leaf.SetErr(io.Discard)
|
||||
if err := corecmd.ConfirmSafety(leaf, *final.Safety); !isConfirmationGateError(err) {
|
||||
fails = append(fails, row{canonical, cliPath, "", fmt.Sprintf(
|
||||
"framework leaf: ConfirmSafety(ContractFinal.Safety) = %v, want confirmation gate", err)})
|
||||
continue
|
||||
}
|
||||
// Execute without --yes must not succeed (confirm-first or validate-then-confirm).
|
||||
if err := probeConfirmationGate(leaf); err == nil {
|
||||
fails = append(fails, row{canonical, cliPath, "",
|
||||
"framework leaf: Execute without --yes succeeded"})
|
||||
continue
|
||||
}
|
||||
gate = "framework_confirm_safety"
|
||||
}
|
||||
okRows = append(okRows, row{canonical, cliPath, gate, "ok"})
|
||||
}
|
||||
|
||||
if checked == 0 {
|
||||
t.Fatal("no user_required ContractFinal leaves found")
|
||||
}
|
||||
if len(fails) != 0 {
|
||||
for _, f := range fails {
|
||||
t.Errorf("%s (%s): %s", f.canonical, f.cliPath, f.detail)
|
||||
}
|
||||
t.Fatalf("Safety homology failed: %d/%d user_required leaves", len(fails), checked)
|
||||
}
|
||||
|
||||
byGate := map[string]int{}
|
||||
for _, r := range okRows {
|
||||
byGate[r.gate]++
|
||||
}
|
||||
t.Logf("user_required Safety homology OK: %d leaves gates=%v", checked, byGate)
|
||||
|
||||
// Sheet transitional dual gate: every Sheet --yes-only protect marker must
|
||||
// also carry DeclareLeafMetadata ConfirmSafety. A bare sheet_protect leaf
|
||||
// would mean the outer guard was dropped from the homology pair (or the
|
||||
// contract wrap never installed), silently widening authorization.
|
||||
if bare := byGate["sheet_protect"]; bare != 0 {
|
||||
t.Fatalf("transitional Sheet dual gate broken: %d leaf(ves) have sheet_protect without declare_leaf_confirm", bare)
|
||||
}
|
||||
if dual := byGate["declare_leaf+sheet_marker"]; dual == 0 {
|
||||
t.Fatal("expected declare_leaf+sheet_marker leaves for Sheet transitional dual confirmation gate")
|
||||
}
|
||||
|
||||
// Confirm/validate order is asserted by behavior, not by a claim of intent:
|
||||
// a "deferred" annotation can still confirm first at runtime. For every
|
||||
// user_required leaf that publishes a required parameter, an argument-less
|
||||
// invocation must report that missing parameter — reaching confirmation
|
||||
// instead is the RFC §5.1 inversion.
|
||||
//
|
||||
// Two forms declare guard-first ordering deliberately and are exempt: a Spec
|
||||
// ConfirmFirst field (the devapp write contract asserted by
|
||||
// TestEveryDevAppWriteCommandRequiresGuard) and the reviewed Sheet
|
||||
// confirmationGuards table, whose wrapper is --yes-only by design. Both are
|
||||
// declarations, not inferences, so the exemption cannot be reached by
|
||||
// accident.
|
||||
var orderFails []row
|
||||
for _, r := range okRows {
|
||||
leaf := boundByCanon[r.canonical].PrimaryCommand
|
||||
if leaf == nil || !requiresParameter(liveToolByCanon[r.canonical]) {
|
||||
continue
|
||||
}
|
||||
if corecmd.HasDeclaredConfirmFirst(leaf) || helpers.HasSheetMutationConfirmationGuard(leaf) {
|
||||
continue
|
||||
}
|
||||
if err := probeConfirmationGate(leaf); isConfirmationGateError(err) {
|
||||
orderFails = append(orderFails, row{r.canonical, r.cliPath, r.gate,
|
||||
fmt.Sprintf("confirms before reporting a missing required parameter: %v", err)})
|
||||
}
|
||||
}
|
||||
if len(orderFails) != 0 {
|
||||
for _, f := range orderFails {
|
||||
t.Errorf("%s (%s): %s", f.canonical, f.cliPath, f.detail)
|
||||
}
|
||||
t.Fatalf("confirm/validate order invariant failed: %d user_required leaf(ves)", len(orderFails))
|
||||
}
|
||||
}
|
||||
|
||||
// requiresParameter reports whether the delivered ToolSpec publishes at least
|
||||
// one required parameter, i.e. whether an argument-less invocation is expected
|
||||
// to fail validation before anything else.
|
||||
func requiresParameter(tool cli.ToolSpec) bool {
|
||||
for _, param := range tool.Parameters {
|
||||
if param.Required || param.CLIRequired {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func probeConfirmationGate(leaf *cobra.Command) error {
|
||||
root := leaf.Root()
|
||||
if root == nil {
|
||||
root = leaf
|
||||
}
|
||||
root.SilenceErrors = true
|
||||
root.SilenceUsage = true
|
||||
root.SetIn(strings.NewReader(""))
|
||||
root.SetOut(io.Discard)
|
||||
root.SetErr(io.Discard)
|
||||
|
||||
// Rebuild args from CommandPath relative to root.
|
||||
path := strings.TrimSpace(strings.TrimPrefix(leaf.CommandPath(), root.CommandPath()))
|
||||
fields := strings.Fields(path)
|
||||
root.SetArgs(fields)
|
||||
return root.Execute()
|
||||
}
|
||||
|
||||
// isConfirmationGateError reports whether err is the confirmation gate itself.
|
||||
//
|
||||
// It classifies on the machine-readable reason rather than on prose: a Sheet
|
||||
// destructive leaf's missing-flag error embeds the example
|
||||
// "(获得用户确认后加 --yes)", so substring matching reported the confirmation gate
|
||||
// for a correct validation error. The decline path is the one case that carries
|
||||
// no reason, so it is matched by its exact message.
|
||||
func isConfirmationGateError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var typed *apperrors.Error
|
||||
if errors.As(err, &typed) && strings.TrimSpace(typed.Reason) == "confirmation_required" {
|
||||
return true
|
||||
}
|
||||
return strings.Contains(err.Error(), "用户取消了操作")
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func TestAnnotateRuntimeRiskEmbedsContractMarker(t *testing.T) {
|
||||
cmd := &cobra.Command{Use: "x"}
|
||||
AnnotateRuntimeRisk(cmd, "write")
|
||||
got, ok := RuntimeContractRisk(cmd)
|
||||
if !ok || got != "write" {
|
||||
t.Fatalf("RuntimeContractRisk = %q %v", got, ok)
|
||||
}
|
||||
if cmd.Annotations[runtimeSchemaContractAnnotation] != "command" {
|
||||
t.Fatalf("contract marker = %q", cmd.Annotations[runtimeSchemaContractAnnotation])
|
||||
}
|
||||
AnnotateRuntimeRisk(cmd, "")
|
||||
if got, _ = RuntimeContractRisk(cmd); got != "write" {
|
||||
t.Fatalf("empty AnnotateRuntimeRisk must be no-op, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyContractRiskToSafety(t *testing.T) {
|
||||
base := SafetySpec{Idempotency: "unknown", Risk: "low", Confirmation: "not_required", Effect: "read"}
|
||||
got := applyContractRiskToSafety(base, "high-risk-write")
|
||||
if got.Effect != "destructive" || got.Risk != "high" || got.Confirmation != "user_required" {
|
||||
t.Fatalf("high-risk overlay = %#v", got)
|
||||
}
|
||||
if got.Idempotency != "unknown" {
|
||||
t.Fatalf("idempotency should be preserved, got %q", got.Idempotency)
|
||||
}
|
||||
got = applyContractRiskToSafety(base, "write")
|
||||
if got.Effect != "write" || got.Risk != "medium" || got.Confirmation != "user_required" {
|
||||
t.Fatalf("write overlay = %#v", got)
|
||||
}
|
||||
got = applyContractRiskToSafety(base, "read")
|
||||
if got.Effect != "read" || got.Risk != "low" || got.Confirmation != "not_required" {
|
||||
t.Fatalf("read overlay = %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateRuntimeGateDeclareOrAnnotate(t *testing.T) {
|
||||
cmd := &cobra.Command{Use: "x"}
|
||||
if HasDeclaredOrAnnotatedConfirmation(cmd) {
|
||||
t.Fatal("bare command must not claim confirmation coverage")
|
||||
}
|
||||
AnnotateRuntimeGate(cmd, "devAppRequireWriteGuard")
|
||||
got, ok := RuntimeContractGate(cmd)
|
||||
if !ok || got != "devAppRequireWriteGuard" {
|
||||
t.Fatalf("RuntimeContractGate = %q %v", got, ok)
|
||||
}
|
||||
if !HasDeclaredOrAnnotatedConfirmation(cmd) {
|
||||
t.Fatal("annotated gate must satisfy declare-OR-annotate")
|
||||
}
|
||||
if cmd.Annotations[runtimeSchemaContractAnnotation] != "command" {
|
||||
t.Fatalf("contract marker = %q", cmd.Annotations[runtimeSchemaContractAnnotation])
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyContractGateToSafety(t *testing.T) {
|
||||
base := SafetySpec{Confirmation: "not_required", Effect: "read", Risk: "low"}
|
||||
got := applyContractGateToSafety(base, "devAppRequireWriteGuard")
|
||||
if got.Confirmation != "user_required" {
|
||||
t.Fatalf("gate must force user_required, got %#v", got)
|
||||
}
|
||||
if got.Effect != "write" || got.EffectSource != "corecmd.contract_gate" {
|
||||
t.Fatalf("gate effect overlay = %#v", got)
|
||||
}
|
||||
if got.Risk != "medium" {
|
||||
t.Fatalf("gate risk overlay = %#v", got)
|
||||
}
|
||||
reviewed := SafetySpec{Confirmation: "not_required", Effect: "destructive", Risk: "high", EffectSource: "reviewed"}
|
||||
got = applyContractGateToSafety(reviewed, "devAppRequireWriteGuard")
|
||||
if got.Effect != "destructive" || got.Risk != "high" || got.Confirmation != "user_required" {
|
||||
t.Fatalf("gate must keep reviewed effect/risk: %#v", got)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Planned homology gates (docs/flag-help-schema-homology.md §4). Implementations
|
||||
// land incrementally; this test pins the gate ID vocabulary so CI docs and
|
||||
// future policy scripts share one checklist.
|
||||
var plannedHomologyGateIDs = []string{
|
||||
"HOM-P1", // schema parameters names ≡ cobra local flags
|
||||
"HOM-P2", // type/required/default ≡ Contract/cobra; hints cannot win those
|
||||
"HOM-P3", // schema constraints ≡ Contract Constraints projection
|
||||
"HOM-S1", // Risk write/high-risk → confirmation=user_required + help Safety
|
||||
"HOM-S2", // write-guard path must record honest provenance
|
||||
"HOM-S3", // read Risk must not project user_required without exclusion
|
||||
"HOM-I1", // interface_ref/bindings ⊆ Contract flags; MCP meta creates no flags
|
||||
"HOM-D1", // --help Flags ≡ schema leaf parameters
|
||||
}
|
||||
|
||||
func TestHomologyDecisionDocPinsPathAAndGateIDs(t *testing.T) {
|
||||
_, thisFile, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
t.Fatal("runtime.Caller failed")
|
||||
}
|
||||
doc := filepath.Clean(filepath.Join(filepath.Dir(thisFile), "..", "..", "docs", "flag-help-schema-homology.md"))
|
||||
raw, err := os.ReadFile(doc)
|
||||
if err != nil {
|
||||
t.Fatalf("read homology doc: %v", err)
|
||||
}
|
||||
body := string(raw)
|
||||
for _, needle := range []string{
|
||||
"路径 A",
|
||||
"Contract / LeafSpec 为 CLI 表面权威",
|
||||
"必须嵌入进 Schema",
|
||||
"embedContractIntoSchema",
|
||||
"声明 = 最终数据源",
|
||||
"不得:",
|
||||
"把声明序列化成 JSON 注解再解析",
|
||||
"声明体系里再挂「评审字段」并行权威",
|
||||
"声明(declare):写什么、写在哪、投影到哪",
|
||||
"契约字段(算声明)",
|
||||
"编排 / 执行字段(不算声明)",
|
||||
"Schema 全覆盖(`ToolSpec` 无空洞)",
|
||||
"dws.schema.runtime_gate",
|
||||
"mcp_passthrough",
|
||||
"不得覆盖 LeafSpec / Shortcut 主路径",
|
||||
} {
|
||||
if !strings.Contains(body, needle) {
|
||||
t.Fatalf("homology doc missing %q", needle)
|
||||
}
|
||||
}
|
||||
for _, id := range plannedHomologyGateIDs {
|
||||
if !strings.Contains(body, id) {
|
||||
t.Fatalf("homology doc missing planned gate ID %s", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCPPassthroughAdmissionExcludesLeafAndShortcut(t *testing.T) {
|
||||
_, thisFile, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
t.Fatal("runtime.Caller failed")
|
||||
}
|
||||
doc := filepath.Clean(filepath.Join(filepath.Dir(thisFile), "..", "..", "docs", "flag-help-schema-homology.md"))
|
||||
raw, err := os.ReadFile(doc)
|
||||
if err != nil {
|
||||
t.Fatalf("read homology doc: %v", err)
|
||||
}
|
||||
body := string(raw)
|
||||
for _, needle := range []string{
|
||||
"严格 1:1",
|
||||
"全部 `LeafSpec` 命令",
|
||||
"全部 `+shortcut`",
|
||||
"surface_kind=mcp_passthrough",
|
||||
} {
|
||||
if !strings.Contains(body, needle) {
|
||||
t.Fatalf("passthrough admission text missing %q", needle)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,9 +131,12 @@ var generatedParamAliases = []ParamAliasEntry{
|
||||
{
|
||||
CLIPath: "chat +chat-bots",
|
||||
Aliases: map[string]string{
|
||||
"chat": "group",
|
||||
"chat": "group",
|
||||
"chat-id": "group",
|
||||
"conversation-id": "group",
|
||||
"open-conversation-id": "group",
|
||||
},
|
||||
Blocked: []string{"conversation-ids", "dest-conversation-id", "group-id", "group-ids", "group-name", "name", "open-conversation-ids", "source", "src-conversation-id", "target"},
|
||||
Blocked: []string{"conversation-ids", "dest-conversation-id", "group-id", "group-ids", "group-name", "id", "name", "open-conversation-ids", "source", "src-conversation-id", "target"},
|
||||
},
|
||||
{
|
||||
CLIPath: "chat +chat-dismiss",
|
||||
@@ -148,9 +151,12 @@ var generatedParamAliases = []ParamAliasEntry{
|
||||
{
|
||||
CLIPath: "chat +chat-invite-url",
|
||||
Aliases: map[string]string{
|
||||
"chat": "group",
|
||||
"chat": "group",
|
||||
"chat-id": "group",
|
||||
"conversation-id": "group",
|
||||
"open-conversation-id": "group",
|
||||
},
|
||||
Blocked: []string{"conversation-ids", "dest-conversation-id", "group-id", "group-ids", "group-name", "name", "open-conversation-ids", "source", "src-conversation-id", "target"},
|
||||
Blocked: []string{"conversation-ids", "dest-conversation-id", "group-id", "group-ids", "group-name", "id", "name", "open-conversation-ids", "source", "src-conversation-id", "target"},
|
||||
},
|
||||
{
|
||||
CLIPath: "chat +chat-mute",
|
||||
@@ -307,7 +313,11 @@ var generatedParamAliases = []ParamAliasEntry{
|
||||
},
|
||||
{
|
||||
CLIPath: "chat +messages-mget",
|
||||
Blocked: []string{"msg-id", "open-message-id", "ref-msg-id", "src-msg-id"},
|
||||
Aliases: map[string]string{
|
||||
"message-ids": "msg-ids",
|
||||
"open-message-ids": "msg-ids",
|
||||
},
|
||||
Blocked: []string{"message-id", "msg-id", "open-message-id", "ref-msg-id", "src-msg-id"},
|
||||
},
|
||||
{
|
||||
CLIPath: "chat +messages-read-status",
|
||||
@@ -1099,7 +1109,7 @@ var generatedParamAliases = []ParamAliasEntry{
|
||||
"open-conversation-ids": "conversation-ids",
|
||||
"user-ids": "users",
|
||||
},
|
||||
Blocked: []string{"at-user-ids", "chat-id", "group-id", "group-ids", "open-conversation-id", "staff-id", "uid", "userid"},
|
||||
Blocked: []string{"at-user-ids", "chat-id", "conversation-id", "group-id", "group-ids", "open-conversation-id", "staff-id", "uid", "userid"},
|
||||
},
|
||||
{
|
||||
CLIPath: "chat message send",
|
||||
|
||||
@@ -49,6 +49,13 @@ const (
|
||||
runtimeSchemaFlagRequiredAnnotation = "dws.schema.required"
|
||||
runtimeSchemaFlagRequiredWhenAnnotation = "dws.schema.required_when"
|
||||
runtimeSchemaFlagExampleAnnotation = "dws.schema.example"
|
||||
|
||||
// Contract surface (command / LeafSpec) embedded onto the live Cobra leaf so
|
||||
// Schema generation can project parameters/constraints/Risk without a second
|
||||
// source of truth. See docs/flag-help-schema-homology.md.
|
||||
runtimeSchemaContractAnnotation = "dws.schema.contract"
|
||||
runtimeSchemaRiskAnnotation = "dws.schema.risk"
|
||||
runtimeSchemaRuntimeGateAnnotation = "dws.schema.runtime_gate"
|
||||
)
|
||||
|
||||
// RuntimeSchemaConstraints describes cross-parameter rules that cannot be
|
||||
@@ -262,6 +269,16 @@ func AnnotateRuntimeRequiredFlags(cmd *cobra.Command, flagNames ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
// AnnotateRuntimeFlagDescription records the Schema parameter description.
|
||||
func AnnotateRuntimeFlagDescription(cmd *cobra.Command, flagName, description string) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
if flag := runtimeCommandFlag(cmd, flagName); flag != nil {
|
||||
setFlagAnnotation(flag, runtimeSchemaFlagDescriptionAnnotation, strings.TrimSpace(description))
|
||||
}
|
||||
}
|
||||
|
||||
// AnnotateRuntimeFlagRequiredWhen records a conditional CLI requirement. The
|
||||
// expression is descriptive metadata and does not alter Cobra validation.
|
||||
func AnnotateRuntimeFlagRequiredWhen(cmd *cobra.Command, flagName, expression string) {
|
||||
@@ -306,6 +323,128 @@ func AnnotateRuntimeFlagExample(cmd *cobra.Command, flagName, example string) {
|
||||
}
|
||||
}
|
||||
|
||||
// AnnotateRuntimeContract marks a command as carrying a command/LeafSpec
|
||||
// Contract surface. Schema assembly treats embedded flag annotations and, when
|
||||
// set, AnnotateRuntimeRisk as Contract-authored facts (path A homology).
|
||||
func AnnotateRuntimeContract(cmd *cobra.Command) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
setRuntimeCommandAnnotation(cmd, runtimeSchemaContractAnnotation, "command")
|
||||
}
|
||||
|
||||
// AnnotateRuntimeRisk records the Contract Risk string (read|write|high-risk-write)
|
||||
// for Schema Safety projection. Empty risk is a no-op so write-guard leaves that
|
||||
// leave Risk unset keep reviewed hint safety.
|
||||
func AnnotateRuntimeRisk(cmd *cobra.Command, risk string) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
risk = strings.TrimSpace(risk)
|
||||
if risk == "" {
|
||||
return
|
||||
}
|
||||
AnnotateRuntimeContract(cmd)
|
||||
setRuntimeCommandAnnotation(cmd, runtimeSchemaRiskAnnotation, risk)
|
||||
}
|
||||
|
||||
// AnnotateRuntimeGate records a non-Risk confirmation path (e.g.
|
||||
// devAppRequireWriteGuard). Homology rule: every confirmation fact is either
|
||||
// Contract-declared (Risk) or explicitly annotated (this gate / reviewed hints).
|
||||
func AnnotateRuntimeGate(cmd *cobra.Command, gate string) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
gate = strings.TrimSpace(gate)
|
||||
if gate == "" {
|
||||
return
|
||||
}
|
||||
AnnotateRuntimeContract(cmd)
|
||||
setRuntimeCommandAnnotation(cmd, runtimeSchemaRuntimeGateAnnotation, gate)
|
||||
}
|
||||
|
||||
// RuntimeContractRisk returns the Contract Risk annotation when present.
|
||||
func RuntimeContractRisk(cmd *cobra.Command) (string, bool) {
|
||||
if cmd == nil || cmd.Annotations == nil {
|
||||
return "", false
|
||||
}
|
||||
risk := strings.TrimSpace(cmd.Annotations[runtimeSchemaRiskAnnotation])
|
||||
if risk == "" {
|
||||
return "", false
|
||||
}
|
||||
return risk, true
|
||||
}
|
||||
|
||||
// RuntimeContractGate returns the annotated runtime confirmation gate when present.
|
||||
func RuntimeContractGate(cmd *cobra.Command) (string, bool) {
|
||||
if cmd == nil || cmd.Annotations == nil {
|
||||
return "", false
|
||||
}
|
||||
gate := strings.TrimSpace(cmd.Annotations[runtimeSchemaRuntimeGateAnnotation])
|
||||
if gate == "" {
|
||||
return "", false
|
||||
}
|
||||
return gate, true
|
||||
}
|
||||
|
||||
// HasDeclaredOrAnnotatedConfirmation reports whether confirmation semantics are
|
||||
// covered by a typed Contract SafetySpec, the legacy Shortcut Risk bridge, or
|
||||
// an explicit runtime_gate annotation.
|
||||
func HasDeclaredOrAnnotatedConfirmation(cmd *cobra.Command) bool {
|
||||
if final, ok := RuntimeContractFinal(cmd); ok && final.Safety != nil &&
|
||||
strings.TrimSpace(final.Safety.Confirmation) != "" {
|
||||
return true
|
||||
}
|
||||
if _, ok := RuntimeContractRisk(cmd); ok {
|
||||
return true
|
||||
}
|
||||
_, ok := RuntimeContractGate(cmd)
|
||||
return ok
|
||||
}
|
||||
|
||||
// applyContractRiskToSafety overlays Schema Safety fields from an embedded
|
||||
// Contract Risk value. Path A: Contract wins effect/risk/confirmation for the
|
||||
// managed surface; other Safety fields (e.g. idempotency) are preserved.
|
||||
func applyContractRiskToSafety(base SafetySpec, contractRisk string) SafetySpec {
|
||||
out := base
|
||||
switch strings.TrimSpace(contractRisk) {
|
||||
case "write":
|
||||
out.Effect = "write"
|
||||
out.EffectSource = "corecmd.contract"
|
||||
out.Risk = "medium"
|
||||
out.Confirmation = "user_required"
|
||||
case "high-risk-write":
|
||||
out.Effect = "destructive"
|
||||
out.EffectSource = "corecmd.contract"
|
||||
out.Risk = "high"
|
||||
out.Confirmation = "user_required"
|
||||
case "read":
|
||||
out.Effect = "read"
|
||||
out.EffectSource = "corecmd.contract"
|
||||
out.Risk = "low"
|
||||
out.Confirmation = "not_required"
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// applyContractGateToSafety ensures a write-guard annotation cannot leave Schema
|
||||
// claiming confirmation is not required. Reviewed effect/risk are kept when set.
|
||||
func applyContractGateToSafety(base SafetySpec, gate string) SafetySpec {
|
||||
out := base
|
||||
if strings.TrimSpace(gate) == "" {
|
||||
return out
|
||||
}
|
||||
out.Confirmation = "user_required"
|
||||
if out.Effect == "" || out.Effect == "read" {
|
||||
out.Effect = "write"
|
||||
out.EffectSource = "corecmd.contract_gate"
|
||||
}
|
||||
if out.Risk == "" || out.Risk == "low" {
|
||||
out.Risk = "medium"
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// AnnotateRuntimeConstraints records command-level parameter relationships.
|
||||
func AnnotateRuntimeConstraints(cmd *cobra.Command, constraints RuntimeSchemaConstraints) {
|
||||
if cmd == nil {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"tools": {
|
||||
"aisearch behavior": {
|
||||
"agent_summary": "搜索发送/创建/分享/编辑/接收等明确行为记录",
|
||||
"agent_summary_source": "dws-agent-selection/aisearch",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只按主题找内容本身时用 aisearch enterprise",
|
||||
@@ -11,7 +11,7 @@
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "command-verb",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws aisearch behavior --types mail --behavior-type send --direction \"我-\u003e汐峰\" --format json",
|
||||
"dws aisearch behavior --queries \"智能化方案\" --types document --behavior-type create --format json"
|
||||
@@ -19,33 +19,29 @@
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "搜索发送/创建/分享/编辑/接收等明确行为记录",
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "搜索发送/创建/分享/编辑/接收等明确行为记录",
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -60,48 +56,58 @@
|
||||
"只按主题找内容本身时用 aisearch enterprise",
|
||||
"没有行为动作词时不要选用本工具"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"只按主题找内容本身时用 aisearch enterprise",
|
||||
"没有行为动作词时不要选用本工具"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -110,50 +116,52 @@
|
||||
"dws aisearch behavior --types mail --behavior-type send --direction \"我-\u003e汐峰\" --format json",
|
||||
"dws aisearch behavior --queries \"智能化方案\" --types document --behavior-type create --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws aisearch behavior --types mail --behavior-type send --direction \"我-\u003e汐峰\" --format json",
|
||||
"dws aisearch behavior --queries \"智能化方案\" --types document --behavior-type create --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -165,52 +173,61 @@
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": "aisearch.search_enterprise_behavior",
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.aisearch.search_enterprise_behavior.interface_ref",
|
||||
"precedence": "mcp_fallback",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "aisearch.search_enterprise_behavior",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "aisearch.search_enterprise_behavior",
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.aisearch.search_enterprise_behavior.interface_ref",
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"value": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.aisearch.search_enterprise_behavior",
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -218,19 +235,17 @@
|
||||
"value": [
|
||||
"用户明确问我/某人发过、发给、收到、创建、分享、编辑过什么"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户明确问我/某人发过、发给、收到、创建、分享、编辑过什么"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -244,13 +259,8 @@
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
"Cobra help: dws aisearch behavior --help",
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"internal/cli/schema_command_registry.json#aisearch.search_enterprise_behavior",
|
||||
"internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"internal/cli/schema_hints/selection/aisearch.json",
|
||||
"corecmd.SchemaDecl",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aisearch.search_enterprise_behavior",
|
||||
"live-dws-schema:aisearch.search_enterprise_behavior",
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/aisearch.md"
|
||||
],
|
||||
@@ -260,7 +270,7 @@
|
||||
},
|
||||
"aisearch enterprise": {
|
||||
"agent_summary": "搜索企业内部知识与相关内容(文档/IM/日历/待办/纪要/日志/邮件等)",
|
||||
"agent_summary_source": "dws-agent-selection/aisearch",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"问“我发给谁/谁发给我/我创建过”等行为追溯时用 aisearch behavior",
|
||||
@@ -269,7 +279,7 @@
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "command-verb",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws aisearch enterprise --queries \"智能化方案\" --types document --format json",
|
||||
"dws aisearch enterprise --queries \"OKR\" --types mail --time-range \"最近\" --format json"
|
||||
@@ -277,33 +287,29 @@
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "搜索企业内部知识与相关内容(文档/IM/日历/待办/纪要/日志/邮件等)",
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "搜索企业内部知识与相关内容(文档/IM/日历/待办/纪要/日志/邮件等)",
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -319,10 +325,9 @@
|
||||
"企业找人时用 aisearch person",
|
||||
"已知具体资源 ID 要读写时改用对应产品命令"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
@@ -330,26 +335,23 @@
|
||||
"企业找人时用 aisearch person",
|
||||
"已知具体资源 ID 要读写时改用对应产品命令"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "not_required",
|
||||
@@ -361,15 +363,21 @@
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -378,50 +386,52 @@
|
||||
"dws aisearch enterprise --queries \"智能化方案\" --types document --format json",
|
||||
"dws aisearch enterprise --queries \"OKR\" --types mail --time-range \"最近\" --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws aisearch enterprise --queries \"智能化方案\" --types document --format json",
|
||||
"dws aisearch enterprise --queries \"OKR\" --types mail --time-range \"最近\" --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -433,52 +443,62 @@
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": "aisearch.search_enterprise",
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.aisearch.search_enterprise.interface_ref",
|
||||
"precedence": "mcp_fallback",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "aisearch.search_enterprise",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "aisearch.search_enterprise",
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.aisearch.search_enterprise.interface_ref",
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input."
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -487,20 +507,18 @@
|
||||
"按主题找资料、方案、文档、消息、邮件等内容,且关注“有什么内容”",
|
||||
"用户显式给出时间词或类型词时分别映射到 --time-range / --types"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"按主题找资料、方案、文档、消息、邮件等内容,且关注“有什么内容”",
|
||||
"用户显式给出时间词或类型词时分别映射到 --time-range / --types"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -514,13 +532,9 @@
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
"Cobra help: dws aisearch enterprise --help",
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"internal/cli/schema_command_registry.json#aisearch.search_enterprise",
|
||||
"corecmd.SchemaDecl",
|
||||
"internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"internal/cli/schema_hints/selection/aisearch.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aisearch.search_enterprise",
|
||||
"live-dws-schema:aisearch.search_enterprise",
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/aisearch.md"
|
||||
],
|
||||
@@ -531,7 +545,7 @@
|
||||
},
|
||||
"aisearch person": {
|
||||
"agent_summary": "企业内找人:按姓名/部门/职位/职责/上下级/手机号/工号筛选",
|
||||
"agent_summary_source": "dws-agent-selection/aisearch",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"已有 userId 只需详情时用 contact user get",
|
||||
@@ -540,7 +554,7 @@
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "command-verb",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws aisearch person --keyword \"张三\" --dimension name --format json",
|
||||
"dws aisearch person --keyword \"五道\" --dimension supervisor --format json"
|
||||
@@ -548,33 +562,29 @@
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "企业内找人:按姓名/部门/职位/职责/上下级/手机号/工号筛选",
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "企业内找人:按姓名/部门/职位/职责/上下级/手机号/工号筛选",
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -590,10 +600,9 @@
|
||||
"精确通讯录关键词搜同事/好友且不涉及职责语义时可用 contact user search",
|
||||
"搜企业知识内容时用 aisearch enterprise;搜行为记录时用 aisearch behavior"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
@@ -601,38 +610,49 @@
|
||||
"精确通讯录关键词搜同事/好友且不涉及职责语义时可用 contact user search",
|
||||
"搜企业知识内容时用 aisearch enterprise;搜行为记录时用 aisearch behavior"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -641,50 +661,52 @@
|
||||
"dws aisearch person --keyword \"张三\" --dimension name --format json",
|
||||
"dws aisearch person --keyword \"五道\" --dimension supervisor --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws aisearch person --keyword \"张三\" --dimension name --format json",
|
||||
"dws aisearch person --keyword \"五道\" --dimension supervisor --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -696,52 +718,61 @@
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": "aisearch.enterprise_person_search",
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.aisearch.enterprise_person_search.interface_ref",
|
||||
"precedence": "mcp_fallback",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "aisearch.enterprise_person_search",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "aisearch.enterprise_person_search",
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.aisearch.enterprise_person_search.interface_ref",
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/aisearch marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"value": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.aisearch.enterprise_person_search",
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -750,20 +781,18 @@
|
||||
"找人、谁负责某事、查上级/下级、按手机号或工号定位人员",
|
||||
"需要把维度词映射到 --dimension,关键词只保留目标实体"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"找人、谁负责某事、查上级/下级、按手机号或工号定位人员",
|
||||
"需要把维度词映射到 --dimension,关键词只保留目标实体"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aisearch.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -777,13 +806,8 @@
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
"Cobra help: dws aisearch person --help",
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"internal/cli/schema_command_registry.json#aisearch.enterprise_person_search",
|
||||
"internal/cli/schema_hints/metadata/aisearch.json",
|
||||
"internal/cli/schema_hints/selection/aisearch.json",
|
||||
"corecmd.SchemaDecl",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aisearch.enterprise_person_search",
|
||||
"live-dws-schema:aisearch.enterprise_person_search",
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/aisearch.md",
|
||||
"skills/mono/references/products/mail.md"
|
||||
|
||||
+10889
-12931
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"tools": {
|
||||
"audit export": {
|
||||
"agent_summary": "按日期范围导出本地操作审计日志(jsonl 或 csv)",
|
||||
"agent_summary_source": "dws-agent-selection/audit",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只看最近几条用 audit tail",
|
||||
@@ -11,7 +11,7 @@
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "agent-hint",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws audit export --format jsonl",
|
||||
"dws audit export --since 2026-07-01 --until 2026-07-14 --format csv"
|
||||
@@ -19,33 +19,29 @@
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "按日期范围导出本地操作审计日志(jsonl 或 csv)",
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "按日期范围导出本地操作审计日志(jsonl 或 csv)",
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log export from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log export from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -54,52 +50,46 @@
|
||||
"只看最近几条用 audit tail",
|
||||
"只校验哈希链完整性用 audit verify"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"只看最近几条用 audit tail",
|
||||
"只校验哈希链完整性用 audit verify"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log export from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log export from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log export from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log export from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -108,82 +98,74 @@
|
||||
"dws audit export --format jsonl",
|
||||
"dws audit export --since 2026-07-01 --until 2026-07-14 --format csv"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws audit export --format jsonl",
|
||||
"dws audit export --since 2026-07-01 --until 2026-07-14 --format csv"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log export from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log export from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "local",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log export from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "local",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log export from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_reason": {
|
||||
"value": "命令读取并导出本地审计日志文件,不绑定 pinned MCP RPC",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log export from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "命令读取并导出本地审计日志文件,不绑定 pinned MCP RPC",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log export from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode local forbids a direct MCP interface_ref",
|
||||
"candidates": [
|
||||
{
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true,
|
||||
"review_reason": "final interface mode local forbids a direct MCP interface_ref"
|
||||
}
|
||||
@@ -191,40 +173,29 @@
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log export from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log export from #555; no remote RPC."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log export from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log export from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -232,19 +203,17 @@
|
||||
"value": [
|
||||
"需要把本地审计日志导出为 jsonl/csv,或按 --since/--until 取一段时间"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要把本地审计日志导出为 jsonl/csv,或按 --since/--until 取一段时间"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读导出,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -255,11 +224,7 @@
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
"cobra-help:dws audit export --help",
|
||||
"internal/app/audit_command.go",
|
||||
"internal/cli/schema_command_registry.json#audit.export",
|
||||
"internal/cli/schema_hints/metadata/audit.json",
|
||||
"internal/cli/schema_hints/selection/audit.json"
|
||||
"corecmd.SchemaDecl"
|
||||
],
|
||||
"use_when": [
|
||||
"需要把本地审计日志导出为 jsonl/csv,或按 --since/--until 取一段时间"
|
||||
@@ -267,7 +232,7 @@
|
||||
},
|
||||
"audit tail": {
|
||||
"agent_summary": "查看本地操作审计日志最近 N 条记录",
|
||||
"agent_summary_source": "dws-agent-selection/audit",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要按日期范围整段导出用 audit export",
|
||||
@@ -275,7 +240,7 @@
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "agent-hint",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws audit tail",
|
||||
"dws audit tail --lines 50"
|
||||
@@ -283,33 +248,29 @@
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "查看本地操作审计日志最近 N 条记录",
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "查看本地操作审计日志最近 N 条记录",
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -318,52 +279,46 @@
|
||||
"需要按日期范围整段导出用 audit export",
|
||||
"需要校验哈希链用 audit verify"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要按日期范围整段导出用 audit export",
|
||||
"需要校验哈希链用 audit verify"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -372,82 +327,74 @@
|
||||
"dws audit tail",
|
||||
"dws audit tail --lines 50"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws audit tail",
|
||||
"dws audit tail --lines 50"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "local",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "local",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_reason": {
|
||||
"value": "命令读取本地审计日志尾部,不绑定 pinned MCP RPC",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "命令读取本地审计日志尾部,不绑定 pinned MCP RPC",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode local forbids a direct MCP interface_ref",
|
||||
"candidates": [
|
||||
{
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true,
|
||||
"review_reason": "final interface mode local forbids a direct MCP interface_ref"
|
||||
}
|
||||
@@ -455,40 +402,29 @@
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit log tail from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -496,19 +432,17 @@
|
||||
"value": [
|
||||
"需要快速查看最近写入的审计记录(默认最近 20 条)"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要快速查看最近写入的审计记录(默认最近 20 条)"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读查看,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -519,11 +453,7 @@
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
"cobra-help:dws audit tail --help",
|
||||
"internal/app/audit_command.go",
|
||||
"internal/cli/schema_command_registry.json#audit.tail",
|
||||
"internal/cli/schema_hints/metadata/audit.json",
|
||||
"internal/cli/schema_hints/selection/audit.json"
|
||||
"corecmd.SchemaDecl"
|
||||
],
|
||||
"use_when": [
|
||||
"需要快速查看最近写入的审计记录(默认最近 20 条)"
|
||||
@@ -531,14 +461,14 @@
|
||||
},
|
||||
"audit verify": {
|
||||
"agent_summary": "校验本地审计日志文件的哈希链完整性",
|
||||
"agent_summary_source": "dws-agent-selection/audit",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只浏览或导出日志内容时用 audit tail / audit export"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "agent-hint",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws audit verify",
|
||||
"dws audit verify --file /path/to/audit.jsonl"
|
||||
@@ -546,33 +476,29 @@
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "校验本地审计日志文件的哈希链完整性",
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "校验本地审计日志文件的哈希链完整性",
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -580,51 +506,45 @@
|
||||
"value": [
|
||||
"只浏览或导出日志内容时用 audit tail / audit export"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"只浏览或导出日志内容时用 audit tail / audit export"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -633,82 +553,74 @@
|
||||
"dws audit verify",
|
||||
"dws audit verify --file /path/to/audit.jsonl"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws audit verify",
|
||||
"dws audit verify --file /path/to/audit.jsonl"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "local",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "local",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_reason": {
|
||||
"value": "命令校验本地审计日志哈希链,不绑定 pinned MCP RPC",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "命令校验本地审计日志哈希链,不绑定 pinned MCP RPC",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode local forbids a direct MCP interface_ref",
|
||||
"candidates": [
|
||||
{
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true,
|
||||
"review_reason": "final interface mode local forbids a direct MCP interface_ref"
|
||||
}
|
||||
@@ -716,40 +628,29 @@
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "internal/cli/schema_hints/metadata/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Local audit hash-chain verify from #555; no remote RPC."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -757,19 +658,17 @@
|
||||
"value": [
|
||||
"怀疑审计文件被篡改,或需要确认最新/指定文件哈希链是否完整"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"怀疑审计文件被篡改,或需要确认最新/指定文件哈希链是否完整"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/audit.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "纳入 #555 公开 audit leaf 的 Agent 选型文案;本地只读校验,示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -780,11 +679,7 @@
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
"cobra-help:dws audit verify --help",
|
||||
"internal/app/audit_command.go",
|
||||
"internal/cli/schema_command_registry.json#audit.verify",
|
||||
"internal/cli/schema_hints/metadata/audit.json",
|
||||
"internal/cli/schema_hints/selection/audit.json"
|
||||
"corecmd.SchemaDecl"
|
||||
],
|
||||
"use_when": [
|
||||
"怀疑审计文件被篡改,或需要确认最新/指定文件哈希链是否完整"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+10098
-20178
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2342
-3335
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"tools": {
|
||||
"devdoc article search": {
|
||||
"agent_summary": "搜索钉钉开放平台开发文档,返回资料与链接(不生成分析答案)",
|
||||
"agent_summary_source": "dws-agent-selection/devdoc",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"搜索用户业务文档用 drive/wiki/doc,不要用 devdoc",
|
||||
@@ -11,7 +11,7 @@
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "command-verb",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws devdoc article search --query \"OAuth2 接入\" --format json",
|
||||
"dws devdoc article search --query \"errcode 40078\" --format json"
|
||||
@@ -19,33 +19,29 @@
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "搜索钉钉开放平台开发文档,返回资料与链接(不生成分析答案)",
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "搜索钉钉开放平台开发文档,返回资料与链接(不生成分析答案)",
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -60,36 +56,32 @@
|
||||
"搜索用户业务文档用 drive/wiki/doc,不要用 devdoc",
|
||||
"要执行开放平台应用配置变更时用 dev"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"搜索用户业务文档用 drive/wiki/doc,不要用 devdoc",
|
||||
"要执行开放平台应用配置变更时用 dev"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "not_required",
|
||||
@@ -101,15 +93,21 @@
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -118,50 +116,52 @@
|
||||
"dws devdoc article search --query \"OAuth2 接入\" --format json",
|
||||
"dws devdoc article search --query \"errcode 40078\" --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws devdoc article search --query \"OAuth2 接入\" --format json",
|
||||
"dws devdoc article search --query \"errcode 40078\" --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -173,15 +173,21 @@
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": "devdoc.search_open_platform_docs",
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"precedence": "imported",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "devdoc.search_open_platform_docs",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "devdoc.search_open_platform_docs",
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"precedence": "imported",
|
||||
"selected": true
|
||||
"selected": false
|
||||
},
|
||||
{
|
||||
"value": "devdoc.search_open_platform_docs",
|
||||
@@ -193,38 +199,42 @@
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword."
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -232,19 +242,17 @@
|
||||
"value": [
|
||||
"查 OpenAPI、字段、错误码、OAuth2、接入指南等开放平台开发问题"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"查 OpenAPI、字段、错误码、OAuth2、接入指南等开放平台开发问题"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -258,20 +266,13 @@
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=devdoc.search_open_platform_docs_rag",
|
||||
"Skill:skills/mono/references/products/devdoc.md",
|
||||
"cobra-help:dws devdoc article search",
|
||||
"corecmd.SchemaDecl",
|
||||
"dws-wukong-envelope@4574f7022c32:dws-wukong-discovery.devdoc.prod.json#devdoc.search_open_platform_docs",
|
||||
"internal/cli/schema_command_registry.json#devdoc.search_open_platform_docs_rag",
|
||||
"internal/cli/schema_hints/imported/wukong.json",
|
||||
"internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"internal/cli/schema_hints/selection/devdoc.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.devdoc.search_open_platform_docs_rag",
|
||||
"live-dws-schema:devdoc.search_open_platform_docs_rag",
|
||||
"skills/mono/references/products/devdoc.md",
|
||||
"skills/mono/references/products/simple.md",
|
||||
"structured-hint:internal/cli/schema_hints/imported/wukong.json#devdoc.search_open_platform_docs_rag",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#devdoc.search_open_platform_docs_rag"
|
||||
"skills/mono/references/products/simple.md"
|
||||
],
|
||||
"use_when": [
|
||||
"查 OpenAPI、字段、错误码、OAuth2、接入指南等开放平台开发问题"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+4495
-5805
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:f239237a9b87fa5a95a0520b2e2f2a112e117b273de8418763ba0ecd8652b317",
|
||||
"surface_hash": "sha256:4897d582a472d9d3596f88fb4546e7795d6b9bc334d020712f5d895ce894f7a7",
|
||||
"source_hash": "sha256:dc81f9a6bf1c4137d03f3b0aa7d2b17fcd72c5defdfc10ad26ee4911c4257098",
|
||||
"surface_hash": "sha256:60eee8e2f37d6d9d60689efce85082798eb9ad38b7ba7c0b471c3de676a85a16",
|
||||
"coverage": {
|
||||
"surface_products": 26,
|
||||
"products_with_metadata": 26,
|
||||
"surface_tools": 875,
|
||||
"tools_with_metadata": 875,
|
||||
"tools_with_agent_summary": 875,
|
||||
"tools_with_use_when": 875,
|
||||
"tools_with_avoid_when": 875,
|
||||
"tools_with_examples": 875,
|
||||
"tools_with_interface_mode": 875,
|
||||
"unmatched_skill_tools": 97,
|
||||
"unreviewed_skill_tools": 12
|
||||
"surface_tools": 845,
|
||||
"tools_with_metadata": 845,
|
||||
"tools_with_agent_summary": 845,
|
||||
"tools_with_use_when": 845,
|
||||
"tools_with_avoid_when": 845,
|
||||
"tools_with_examples": 845,
|
||||
"tools_with_interface_mode": 845,
|
||||
"unmatched_skill_tools": 118,
|
||||
"unreviewed_skill_tools": 7
|
||||
},
|
||||
"products": {
|
||||
"aisearch": {
|
||||
@@ -88,20 +88,20 @@
|
||||
]
|
||||
},
|
||||
"aitable": {
|
||||
"agent_summary": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"agent_summary": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"avoid_when": [
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"value": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"value": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true
|
||||
@@ -110,7 +110,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -118,7 +118,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -128,7 +128,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -136,7 +136,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -153,11 +153,10 @@
|
||||
"internal/cli/schema_hints/selection/aitable.json",
|
||||
"skills/mono/SKILL.md",
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md"
|
||||
"skills/mono/references/products/aitable.md"
|
||||
],
|
||||
"use_when": [
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
]
|
||||
},
|
||||
"attendance": {
|
||||
|
||||
@@ -3,47 +3,43 @@
|
||||
"tools": {
|
||||
"live stream list": {
|
||||
"agent_summary": "查看当前用户发起的直播列表与基础统计",
|
||||
"agent_summary_source": "dws-agent-selection/live",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要创建/开播/结束直播时不要使用;当前公开面仅列表查询"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "command-verb",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws live stream list"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "查看当前用户发起的直播列表与基础统计",
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "查看当前用户发起的直播列表与基础统计",
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -57,46 +53,44 @@
|
||||
"value": [
|
||||
"需要创建/开播/结束直播时不要使用;当前公开面仅列表查询"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要创建/开播/结束直播时不要使用;当前公开面仅列表查询"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
@@ -105,49 +99,45 @@
|
||||
"value": [
|
||||
"dws live stream list"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws live stream list"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -159,15 +149,21 @@
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": "live.get_my_lives",
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"precedence": "imported",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "live.get_my_lives",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "live.get_my_lives",
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"precedence": "imported",
|
||||
"selected": true
|
||||
"selected": false
|
||||
},
|
||||
{
|
||||
"value": "live.get_my_lives",
|
||||
@@ -179,37 +175,28 @@
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed"
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
@@ -218,19 +205,17 @@
|
||||
"value": [
|
||||
"用户要看自己发起过的直播、状态或观看量等列表信息"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户要看自己发起过的直播、状态或观看量等列表信息"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -244,19 +229,10 @@
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=live.get_my_lives",
|
||||
"Skill:skills/multi/dingtalk-live/SKILL.md",
|
||||
"cobra-help:dws live stream list",
|
||||
"corecmd.SchemaDecl",
|
||||
"dws-wukong-envelope@4574f7022c32:dws-wukong-discovery.live.prod.json#live.get_my_lives",
|
||||
"internal/cli/schema_command_registry.json#live.get_my_lives",
|
||||
"internal/cli/schema_hints/imported/wukong.json",
|
||||
"internal/cli/schema_hints/metadata/live.json",
|
||||
"internal/cli/schema_hints/selection/live.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.live.get_my_lives",
|
||||
"live-dws-schema:live.get_my_lives",
|
||||
"skills/mono/references/products/live.md",
|
||||
"structured-hint:internal/cli/schema_hints/imported/wukong.json#live.get_my_lives",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#live.get_my_lives"
|
||||
"internal/cli/schema_mcp_metadata.json#tools.live.get_my_lives"
|
||||
],
|
||||
"use_when": [
|
||||
"用户要看自己发起过的直播、状态或观看量等列表信息"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"tools": {
|
||||
"mcp url get": {
|
||||
"agent_summary": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"agent_summary_source": "dws-agent-selection/mcp",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
@@ -11,40 +11,36 @@
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "agent-hint",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -53,52 +49,46 @@
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -106,81 +96,73 @@
|
||||
"value": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "composite",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "composite",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_reason": {
|
||||
"value": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
|
||||
"candidates": [
|
||||
{
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true,
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
|
||||
}
|
||||
@@ -188,40 +170,29 @@
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "medium",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "medium",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -229,19 +200,17 @@
|
||||
"value": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -252,12 +221,7 @@
|
||||
"reviewed": true,
|
||||
"risk": "medium",
|
||||
"source_refs": [
|
||||
"cobra-help:dws mcp url get --help",
|
||||
"internal/app/mcp_url_command.go",
|
||||
"internal/cli/schema_command_registry.json#mcp.url_get",
|
||||
"internal/cli/schema_hints/metadata/mcp.json",
|
||||
"internal/cli/schema_hints/selection/mcp.json",
|
||||
"pkg/edition/default.go#openSupplementServers"
|
||||
"corecmd.SchemaDecl"
|
||||
],
|
||||
"use_when": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1729
-1950
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,14 @@
|
||||
"tools": {
|
||||
"pat browser-policy": {
|
||||
"agent_summary": "配置 PAT 授权流程是否允许打开本地浏览器",
|
||||
"agent_summary_source": "dws-agent-selection/pat",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要授予产品 scope 时用 pat chmod,而不是改浏览器策略"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
"effect_source": "agent-hint",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws pat browser-policy --enabled --format json",
|
||||
"dws pat browser-policy --enabled=false --agentCode \u003cAGENT_CODE\u003e --format json"
|
||||
@@ -18,33 +18,29 @@
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "配置 PAT 授权流程是否允许打开本地浏览器",
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "配置 PAT 授权流程是否允许打开本地浏览器",
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -52,49 +48,51 @@
|
||||
"value": [
|
||||
"需要授予产品 scope 时用 pat chmod,而不是改浏览器策略"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要授予产品 scope 时用 pat chmod,而不是改浏览器策略"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "write",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "write",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "write",
|
||||
@@ -109,80 +107,80 @@
|
||||
"dws pat browser-policy --enabled --format json",
|
||||
"dws pat browser-policy --enabled=false --agentCode \u003cAGENT_CODE\u003e --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws pat browser-policy --enabled --format json",
|
||||
"dws pat browser-policy --enabled=false --agentCode \u003cAGENT_CODE\u003e --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "unknown",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "unknown",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "unknown",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "local",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "local",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_reason": {
|
||||
"value": "命令仅操作本地进程或策略文件,不调用 MCP 接口",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "命令仅操作本地进程或策略文件,不调用 MCP 接口",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode local forbids a direct MCP interface_ref",
|
||||
"candidates": [
|
||||
{
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true,
|
||||
"review_reason": "final interface mode local forbids a direct MCP interface_ref"
|
||||
}
|
||||
@@ -190,40 +188,29 @@
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "medium",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "medium",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "medium",
|
||||
@@ -237,19 +224,17 @@
|
||||
"value": [
|
||||
"需要允许或禁止某 Agent 在 PAT 授权时打开浏览器"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要允许或禁止某 Agent 在 PAT 授权时打开浏览器"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -260,15 +245,8 @@
|
||||
"reviewed": true,
|
||||
"risk": "medium",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=pat.browser_policy",
|
||||
"Skill:skills/mono/references/products/pat.md",
|
||||
"cobra-help:dws pat browser-policy",
|
||||
"internal/cli/schema_command_registry.json#pat.browser_policy",
|
||||
"internal/cli/schema_hints/metadata/pat.json",
|
||||
"internal/cli/schema_hints/selection/pat.json",
|
||||
"live-dws-schema:pat.browser_policy#FAILED",
|
||||
"skills/mono/references/products/pat.md",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#pat.browser_policy"
|
||||
"corecmd.SchemaDecl",
|
||||
"skills/mono/references/products/pat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要允许或禁止某 Agent 在 PAT 授权时打开浏览器"
|
||||
@@ -276,7 +254,7 @@
|
||||
},
|
||||
"pat chmod": {
|
||||
"agent_summary": "预览或执行 PAT 批量行为授权(支持 dryRun / pending flow)",
|
||||
"agent_summary_source": "dws-agent-selection/pat",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"普通 OAuth 登录用 auth,不要用 pat",
|
||||
@@ -285,40 +263,36 @@
|
||||
],
|
||||
"confirmation": "user_required",
|
||||
"effect": "write",
|
||||
"effect_source": "agent-hint",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws pat chmod --products calendar,aitable --grant-type session --session-id \u003cSESSION_ID\u003e --dry-run --format json"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "预览或执行 PAT 批量行为授权(支持 dryRun / pending flow)",
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "预览或执行 PAT 批量行为授权(支持 dryRun / pending flow)",
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -334,10 +308,9 @@
|
||||
"授权产品、grant-type 或 session-id 未明确时不要执行写入",
|
||||
"只要改本地浏览器打开策略时用 pat browser-policy"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
@@ -345,26 +318,23 @@
|
||||
"授权产品、grant-type 或 session-id 未明确时不要执行写入",
|
||||
"只要改本地浏览器打开策略时用 pat browser-policy"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "user_required",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "user_required",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "user_required",
|
||||
@@ -377,17 +347,15 @@
|
||||
},
|
||||
"effect": {
|
||||
"value": "write",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "write",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "write",
|
||||
@@ -408,49 +376,51 @@
|
||||
"value": [
|
||||
"dws pat chmod --products calendar,aitable --grant-type session --session-id \u003cSESSION_ID\u003e --dry-run --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws pat chmod --products calendar,aitable --grant-type session --session-id \u003cSESSION_ID\u003e --dry-run --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "unknown",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "unknown",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "unknown",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -462,17 +432,15 @@
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": "pat.pat.batch_grant",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "pat.pat.batch_grant",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "pat.pat.batch_grant",
|
||||
@@ -484,40 +452,35 @@
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"value": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.pat.batch_grant",
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "high",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "high",
|
||||
"source": "internal/cli/schema_hints/metadata/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/pat marks this tool as reviewed"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "high",
|
||||
@@ -533,20 +496,18 @@
|
||||
"命令提示缺少行为授权,需要按产品或 scope 批量授权",
|
||||
"先 --dry-run 查看 selected/skipped/pending,用户确认后再执行"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"命令提示缺少行为授权,需要按产品或 scope 批量授权",
|
||||
"先 --dry-run 查看 selected/skipped/pending,用户确认后再执行"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/pat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。"
|
||||
"source": "corecmd.contract",
|
||||
"precedence": "contract_final",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -560,18 +521,9 @@
|
||||
"reviewed": true,
|
||||
"risk": "high",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=pat.batch_grant",
|
||||
"Skill:skills/mono/references/products/pat.md",
|
||||
"cobra-help:dws pat chmod",
|
||||
"helper:internal/pat/chmod.go#typed_yes",
|
||||
"internal/cli/schema_command_registry.json#pat.batch_grant",
|
||||
"internal/cli/schema_hints/metadata/pat.json",
|
||||
"internal/cli/schema_hints/selection/pat.json",
|
||||
"corecmd.SchemaDecl",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.pat.batch_grant",
|
||||
"live-dws-schema:pat.batch_grant#FAILED",
|
||||
"pinned-mcp:pat.batch_grant",
|
||||
"skills/mono/references/products/pat.md",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#pat.batch_grant"
|
||||
"skills/mono/references/products/pat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"命令提示缺少行为授权,需要按产品或 scope 批量授权",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,8 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var marshalManualAgentSelectionFixture = json.Marshal
|
||||
@@ -71,17 +73,27 @@ func BuildManualAgentSelectionEvalFixture(bound BoundCommandRegistry, hints Manu
|
||||
fixture := ManualAgentSelectionFixture{Version: manualAgentSelectionFixtureVersion}
|
||||
report := ManualAgentSelectionReport{}
|
||||
expectedTools := make(map[string]bool, len(bound.Commands))
|
||||
declaredTools := map[string]bool{}
|
||||
candidatesByProduct := map[string][]string{}
|
||||
for _, command := range bound.Commands {
|
||||
canonical := strings.TrimSpace(command.CanonicalPath)
|
||||
expectedTools[canonical] = true
|
||||
// Declared tools source selection assertions from the Contract final
|
||||
// overlay (corecmd.SchemaDecl) instead of a hint-file row.
|
||||
if HasRuntimeContractFinal(command.PrimaryCommand) {
|
||||
declaredTools[canonical] = true
|
||||
}
|
||||
productID, _, ok := strings.Cut(canonical, ".")
|
||||
if !ok || strings.TrimSpace(productID) == "" {
|
||||
return fixture, report, fmt.Errorf("agent_hints selection has invalid bound canonical path %q", canonical)
|
||||
}
|
||||
candidatesByProduct[productID] = append(candidatesByProduct[productID], canonical)
|
||||
}
|
||||
if err := validateManualAgentHintExactSet("selection tools", expectedTools, mapKeysManualAgentTools(hints.Tools)); err != nil {
|
||||
hintTools := mapKeysManualAgentTools(hints.Tools)
|
||||
for canonical := range declaredTools {
|
||||
hintTools[canonical] = true
|
||||
}
|
||||
if err := validateManualAgentHintExactSet("selection tools", expectedTools, hintTools); err != nil {
|
||||
return fixture, report, err
|
||||
}
|
||||
for productID := range candidatesByProduct {
|
||||
@@ -105,7 +117,10 @@ func BuildManualAgentSelectionEvalFixture(bound BoundCommandRegistry, hints Manu
|
||||
return fixture, report, err
|
||||
}
|
||||
|
||||
hint := hints.Tools[canonical]
|
||||
hint, hintOK := hints.Tools[canonical]
|
||||
if !hintOK && declaredTools[canonical] {
|
||||
hint = contractFinalSelectionHint(command.PrimaryCommand)
|
||||
}
|
||||
if len(hint.UseWhen) == 0 {
|
||||
return fixture, report, fmt.Errorf("agent_hints tool %s requires at least one positive use_when selection assertion", canonical)
|
||||
}
|
||||
@@ -169,6 +184,23 @@ func ValidateManualAgentSelectionContract(bound BoundCommandRegistry, hints Manu
|
||||
return report, err
|
||||
}
|
||||
|
||||
// contractFinalSelectionHint synthesizes the selection assertions of a
|
||||
// declared tool from its Contract final overlay, so declared tools keep full
|
||||
// semantic-eval coverage without a hint-file row.
|
||||
func contractFinalSelectionHint(command *cobra.Command) ManualAgentToolHint {
|
||||
hint := ManualAgentToolHint{Reviewed: true, Revision: "contract", Reason: "Contract final declaration (corecmd.SchemaDecl)"}
|
||||
payload, ok := RuntimeContractFinal(command)
|
||||
if !ok || payload.Selection == nil {
|
||||
return hint
|
||||
}
|
||||
selection := payload.Selection
|
||||
hint.AgentSummary = selection.AgentSummary
|
||||
hint.UseWhen = selection.UseWhen
|
||||
hint.AvoidWhen = selection.AvoidWhen
|
||||
hint.Examples = selection.Examples
|
||||
return hint
|
||||
}
|
||||
|
||||
func validateManualAgentSelectionBinding(bound BoundCommandRegistry, canonical string, command BoundCommandSpec) error {
|
||||
if command.CanonicalPath != canonical {
|
||||
return fmt.Errorf("agent_hints selection canonical %q resolves to mismatched BoundCommandRegistry entry %q", canonical, command.CanonicalPath)
|
||||
|
||||
+2856
-3147
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+22716
-21279
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+20718
-35745
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+8691
-5230
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,25 +2,12 @@
|
||||
"product": "devdoc",
|
||||
"tools": {
|
||||
"devdoc.search_open_platform_docs_rag": {
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_metadata_source": "corecmd.contract",
|
||||
"agent_source_refs": [
|
||||
"CommandRegistry:canonical_path=devdoc.search_open_platform_docs_rag",
|
||||
"Skill:skills/mono/references/products/devdoc.md",
|
||||
"cobra-help:dws devdoc article search",
|
||||
"dws-wukong-envelope@4574f7022c32:dws-wukong-discovery.devdoc.prod.json#devdoc.search_open_platform_docs",
|
||||
"internal/cli/schema_command_registry.json#devdoc.search_open_platform_docs_rag",
|
||||
"internal/cli/schema_hints/imported/wukong.json",
|
||||
"internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"internal/cli/schema_hints/selection/devdoc.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.devdoc.search_open_platform_docs_rag",
|
||||
"live-dws-schema:devdoc.search_open_platform_docs_rag",
|
||||
"skills/mono/references/products/devdoc.md",
|
||||
"skills/mono/references/products/simple.md",
|
||||
"structured-hint:internal/cli/schema_hints/imported/wukong.json#devdoc.search_open_platform_docs_rag",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#devdoc.search_open_platform_docs_rag"
|
||||
"corecmd.SchemaDecl"
|
||||
],
|
||||
"agent_summary": "搜索钉钉开放平台开发文档,返回资料与链接(不生成分析答案)",
|
||||
"agent_summary_source": "dws-agent-selection/devdoc",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"搜索用户业务文档用 drive/wiki/doc,不要用 devdoc",
|
||||
@@ -41,7 +28,7 @@
|
||||
"description": "按关键词搜索 open.dingtalk.com 文档,支持分页。默认表格输出,可用 -f json 获取完整响应。",
|
||||
"display": "开放平台文档搜索",
|
||||
"effect": "read",
|
||||
"effect_source": "command-verb",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws devdoc article search --query \"OAuth2 接入\" --format json",
|
||||
"dws devdoc article search --query \"errcode 40078\" --format json"
|
||||
@@ -50,58 +37,58 @@
|
||||
"agent_summary": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "搜索钉钉开放平台开发文档,返回资料与链接(不生成分析答案)"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "搜索钉钉开放平台开发文档,返回资料与链接(不生成分析答案)"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"value": "available"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.devdoc.search_open_platform_docs_rag.interface_ref",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "available"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "available"
|
||||
},
|
||||
"avoid_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"搜索用户业务文档用 drive/wiki/doc,不要用 devdoc",
|
||||
"要执行开放平台应用配置变更时用 dev"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"搜索用户业务文档用 drive/wiki/doc,不要用 devdoc",
|
||||
"要执行开放平台应用配置变更时用 dev"
|
||||
@@ -110,100 +97,94 @@
|
||||
"canonical_path": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "command_registry",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "devdoc article search",
|
||||
"value": "devdoc.search_open_platform_docs_rag"
|
||||
},
|
||||
{
|
||||
"precedence": "reviewed_manual",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"selected": false,
|
||||
"source": "reviewed_manual_hint",
|
||||
"source_ref": "devdoc article search",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "devdoc.search_open_platform_docs_rag"
|
||||
}
|
||||
],
|
||||
"precedence": "command_registry",
|
||||
"resolution": "registry_identity",
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "devdoc article search",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "devdoc.search_open_platform_docs_rag"
|
||||
},
|
||||
"confirmation": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"value": "not_required"
|
||||
},
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false,
|
||||
"source": "risk-default",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "not_required"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "not_required"
|
||||
},
|
||||
"description": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "按关键词搜索 open.dingtalk.com 文档,支持分页。默认表格输出,可用 -f json 获取完整响应。"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
"selected": false,
|
||||
"source": "mcp_metadata",
|
||||
"value": "根据关键词搜索钉钉开放平台的开发文档,返回匹配的文档条目列表,包含标题、摘要、文档链接和相关标签。搜索结果按相关性排序。适用于开发者在集成或调试过程中快速查找 API 说明、接入指南、错误码解释等技术资料。"
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "按关键词搜索 open.dingtalk.com 文档,支持分页。默认表格输出,可用 -f json 获取完整响应。"
|
||||
},
|
||||
"effect": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "command-verb",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "read"
|
||||
}
|
||||
],
|
||||
"precedence": "inference_or_default",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "command-verb",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "read"
|
||||
},
|
||||
"examples": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"dws devdoc article search --query \"OAuth2 接入\" --format json",
|
||||
"dws devdoc article search --query \"errcode 40078\" --format json"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"dws devdoc article search --query \"OAuth2 接入\" --format json",
|
||||
"dws devdoc article search --query \"errcode 40078\" --format json"
|
||||
@@ -212,60 +193,58 @@
|
||||
"idempotency": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "effect-default",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "idempotent"
|
||||
}
|
||||
],
|
||||
"precedence": "inference_or_default",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "effect-default",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "idempotent"
|
||||
},
|
||||
"interface_mode": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"value": "mcp"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.devdoc.search_open_platform_docs_rag.interface_ref",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "mcp"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "mcp"
|
||||
},
|
||||
"interface_ref": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "imported",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": {
|
||||
"product_id": "devdoc",
|
||||
"rpc_name": "search_open_platform_docs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.devdoc.search_open_platform_docs_rag.interface_ref",
|
||||
"value": "devdoc.search_open_platform_docs"
|
||||
}
|
||||
],
|
||||
"precedence": "imported",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": {
|
||||
"product_id": "devdoc",
|
||||
"rpc_name": "search_open_platform_docs"
|
||||
@@ -274,90 +253,93 @@
|
||||
"metadata_source": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "derived_resolution",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "metadata_source_resolution",
|
||||
"value": "embedded-mcp-metadata"
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "corecmd.contract"
|
||||
}
|
||||
],
|
||||
"precedence": "derived_resolution",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "metadata_source_resolution",
|
||||
"value": "embedded-mcp-metadata"
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "corecmd.contract"
|
||||
},
|
||||
"reviewed": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Search text may be supplied as positional keyword or public --query; hidden --keyword remains a runtime compatibility alias and is intentionally not published as a parameter. The versioned binding owns query -\u003e keyword.",
|
||||
"source": "internal/cli/schema_hints/metadata/devdoc.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": true
|
||||
},
|
||||
"risk": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "effect-default",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "low"
|
||||
}
|
||||
],
|
||||
"precedence": "inference_or_default",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "effect-default",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "low"
|
||||
},
|
||||
"title": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "搜索开放平台文档"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
"selected": false,
|
||||
"source": "mcp_metadata",
|
||||
"value": "search_open_platform_docs"
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "搜索开放平台文档"
|
||||
},
|
||||
"use_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"查 OpenAPI、字段、错误码、OAuth2、接入指南等开放平台开发问题"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"source": "internal/cli/schema_hints/selection/devdoc.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"查 OpenAPI、字段、错误码、OAuth2、接入指南等开放平台开发问题"
|
||||
]
|
||||
@@ -372,7 +354,7 @@
|
||||
"rpc_name": "search_open_platform_docs"
|
||||
},
|
||||
"is_alias": false,
|
||||
"metadata_source": "embedded-mcp-metadata",
|
||||
"metadata_source": "corecmd.contract",
|
||||
"name": "search_open_platform_docs_rag",
|
||||
"parameter_count": 3,
|
||||
"parameters": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+9741
-8657
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,24 +2,12 @@
|
||||
"product": "live",
|
||||
"tools": {
|
||||
"live.get_my_lives": {
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_metadata_source": "corecmd.contract",
|
||||
"agent_source_refs": [
|
||||
"CommandRegistry:canonical_path=live.get_my_lives",
|
||||
"Skill:skills/multi/dingtalk-live/SKILL.md",
|
||||
"cobra-help:dws live stream list",
|
||||
"dws-wukong-envelope@4574f7022c32:dws-wukong-discovery.live.prod.json#live.get_my_lives",
|
||||
"internal/cli/schema_command_registry.json#live.get_my_lives",
|
||||
"internal/cli/schema_hints/imported/wukong.json",
|
||||
"internal/cli/schema_hints/metadata/live.json",
|
||||
"internal/cli/schema_hints/selection/live.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.live.get_my_lives",
|
||||
"live-dws-schema:live.get_my_lives",
|
||||
"skills/mono/references/products/live.md",
|
||||
"structured-hint:internal/cli/schema_hints/imported/wukong.json#live.get_my_lives",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#live.get_my_lives"
|
||||
"corecmd.SchemaDecl"
|
||||
],
|
||||
"agent_summary": "查看当前用户发起的直播列表与基础统计",
|
||||
"agent_summary_source": "dws-agent-selection/live",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要创建/开播/结束直播时不要使用;当前公开面仅列表查询"
|
||||
@@ -28,10 +16,10 @@
|
||||
"cli_name": "list",
|
||||
"cli_path": "live stream list",
|
||||
"confirmation": "not_required",
|
||||
"description": "查看我的直播列表",
|
||||
"description": "查看当前用户发起的直播列表与基础统计",
|
||||
"display": "直播列表 / 信息",
|
||||
"effect": "read",
|
||||
"effect_source": "command-verb",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws live stream list"
|
||||
],
|
||||
@@ -39,57 +27,57 @@
|
||||
"agent_summary": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "查看当前用户发起的直播列表与基础统计"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "查看当前用户发起的直播列表与基础统计"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"value": "available"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.live.get_my_lives.interface_ref",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "available"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "available"
|
||||
},
|
||||
"avoid_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"需要创建/开播/结束直播时不要使用;当前公开面仅列表查询"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"需要创建/开播/结束直播时不要使用;当前公开面仅列表查询"
|
||||
]
|
||||
@@ -97,83 +85,93 @@
|
||||
"canonical_path": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "command_registry",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "live stream list",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "live.get_my_lives"
|
||||
}
|
||||
],
|
||||
"precedence": "command_registry",
|
||||
"resolution": "registry_identity",
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "live stream list",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "live.get_my_lives"
|
||||
},
|
||||
"confirmation": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "risk-default",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "not_required"
|
||||
}
|
||||
],
|
||||
"precedence": "inference_or_default",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "risk-default",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "not_required"
|
||||
},
|
||||
"description": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"value": "查看我的直播列表"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
"selected": false,
|
||||
"source": "mcp_metadata",
|
||||
"value": "获取当前用户发起的直播列表,包含每场直播的基础信息与统计数据。返回字段包括:直播标题、封面图 URL、直播 ID、主播昵称、开始/结束时间戳、播放时长(毫秒)、观看量(UV)及直播状态(如已结束)。数据按分页返回,支持判断是否还有更多内容。适用于个人直播管理、历史记录查看等场景,仅展示当前用户有权限访问的直播内容。"
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "查看当前用户发起的直播列表与基础统计"
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"value": "查看我的直播列表"
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "查看当前用户发起的直播列表与基础统计"
|
||||
},
|
||||
"effect": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "command-verb",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "read"
|
||||
}
|
||||
],
|
||||
"precedence": "inference_or_default",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "command-verb",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "read"
|
||||
},
|
||||
"examples": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"dws live stream list"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"dws live stream list"
|
||||
]
|
||||
@@ -181,60 +179,58 @@
|
||||
"idempotency": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "effect-default",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "idempotent"
|
||||
}
|
||||
],
|
||||
"precedence": "inference_or_default",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "effect-default",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "idempotent"
|
||||
},
|
||||
"interface_mode": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"value": "mcp"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.live.get_my_lives.interface_ref",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "mcp"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "mcp"
|
||||
},
|
||||
"interface_ref": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "imported",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": {
|
||||
"product_id": "live",
|
||||
"rpc_name": "get_my_lives"
|
||||
}
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_fallback",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_mcp_metadata.json#tools.live.get_my_lives.interface_ref",
|
||||
"value": "live.get_my_lives"
|
||||
}
|
||||
],
|
||||
"precedence": "imported",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": {
|
||||
"product_id": "live",
|
||||
"rpc_name": "get_my_lives"
|
||||
@@ -243,90 +239,93 @@
|
||||
"metadata_source": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "derived_resolution",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "metadata_source_resolution",
|
||||
"value": "embedded-mcp-metadata"
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "corecmd.contract"
|
||||
}
|
||||
],
|
||||
"precedence": "derived_resolution",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "metadata_source_resolution",
|
||||
"value": "embedded-mcp-metadata"
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "corecmd.contract"
|
||||
},
|
||||
"reviewed": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/live marks this tool as reviewed",
|
||||
"source": "internal/cli/schema_hints/metadata/live.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": true
|
||||
},
|
||||
"risk": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "effect-default",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "low"
|
||||
}
|
||||
],
|
||||
"precedence": "inference_or_default",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "effect-default",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "low"
|
||||
},
|
||||
"title": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "查看我的直播列表"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
"selected": false,
|
||||
"source": "mcp_metadata",
|
||||
"value": "获取当前用户发起的直播列表"
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "查看我的直播列表"
|
||||
},
|
||||
"use_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"用户要看自己发起过的直播、状态或观看量等列表信息"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
|
||||
"source": "internal/cli/schema_hints/selection/live.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"用户要看自己发起过的直播、状态或观看量等列表信息"
|
||||
]
|
||||
@@ -341,7 +340,7 @@
|
||||
"rpc_name": "get_my_lives"
|
||||
},
|
||||
"is_alias": false,
|
||||
"metadata_source": "embedded-mcp-metadata",
|
||||
"metadata_source": "corecmd.contract",
|
||||
"name": "get_my_lives",
|
||||
"parameter_count": 0,
|
||||
"parameters": {},
|
||||
|
||||
+6370
-5803
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,17 +2,12 @@
|
||||
"product": "mcp",
|
||||
"tools": {
|
||||
"mcp.url_get": {
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_metadata_source": "corecmd.contract",
|
||||
"agent_source_refs": [
|
||||
"cobra-help:dws mcp url get --help",
|
||||
"internal/app/mcp_url_command.go",
|
||||
"internal/cli/schema_command_registry.json#mcp.url_get",
|
||||
"internal/cli/schema_hints/metadata/mcp.json",
|
||||
"internal/cli/schema_hints/selection/mcp.json",
|
||||
"pkg/edition/default.go#openSupplementServers"
|
||||
"corecmd.SchemaDecl"
|
||||
],
|
||||
"agent_summary": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"agent_summary_source": "dws-agent-selection/mcp",
|
||||
"agent_summary_source": "corecmd.SchemaDecl",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
@@ -25,7 +20,7 @@
|
||||
"description": "输入 MCP 市场 mcpId,返回以当前用户和组织身份访问该 MCP 的 Streamable HTTP 服务地址。\n\n安全提示:返回的 mcpURL 和 mcpJSON 可能包含身份凭据,仅限个人使用,请勿分享到群聊、文档、邮件、代码仓库或日志。",
|
||||
"display": "管理 MCP 服务连接信息",
|
||||
"effect": "read",
|
||||
"effect_source": "agent-hint",
|
||||
"effect_source": "corecmd.contract",
|
||||
"examples": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
@@ -33,52 +28,58 @@
|
||||
"agent_summary": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "available"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "available"
|
||||
},
|
||||
"avoid_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
@@ -87,81 +88,93 @@
|
||||
"canonical_path": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "command_registry",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "mcp url get",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "mcp.url_get"
|
||||
}
|
||||
],
|
||||
"precedence": "command_registry",
|
||||
"resolution": "registry_identity",
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "mcp url get",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "mcp.url_get"
|
||||
},
|
||||
"confirmation": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "not_required"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "not_required"
|
||||
},
|
||||
"description": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "输入 MCP 市场 mcpId,返回以当前用户和组织身份访问该 MCP 的 Streamable HTTP 服务地址。\n\n安全提示:返回的 mcpURL 和 mcpJSON 可能包含身份凭据,仅限个人使用,请勿分享到群聊、文档、邮件、代码仓库或日志。"
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "输入 MCP 市场 mcpId,返回以当前用户和组织身份访问该 MCP 的 Streamable HTTP 服务地址。\n\n安全提示:返回的 mcpURL 和 mcpJSON 可能包含身份凭据,仅限个人使用,请勿分享到群聊、文档、邮件、代码仓库或日志。"
|
||||
},
|
||||
"effect": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "read"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "read"
|
||||
},
|
||||
"examples": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
]
|
||||
@@ -169,136 +182,165 @@
|
||||
"idempotency": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "idempotent"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "idempotent"
|
||||
},
|
||||
"interface_mode": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "composite"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "composite"
|
||||
},
|
||||
"interface_reason": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata."
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata."
|
||||
},
|
||||
"interface_ref": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": null
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": null
|
||||
},
|
||||
"metadata_source": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "corecmd.contract"
|
||||
}
|
||||
],
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "corecmd.contract"
|
||||
},
|
||||
"reviewed": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": true
|
||||
},
|
||||
"risk": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "medium"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "medium"
|
||||
},
|
||||
"title": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "按 mcpId 获取 MCP 的 Streamable HTTP 服务地址"
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": "按 mcpId 获取 MCP 的 Streamable HTTP 服务地址"
|
||||
},
|
||||
"use_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"precedence": "contract_final",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "contract_final",
|
||||
"resolution": "contract_pass_through",
|
||||
"review_reason": "Contract final Schema pass-through",
|
||||
"source": "corecmd.contract",
|
||||
"source_ref": "corecmd.SchemaDecl",
|
||||
"value": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
]
|
||||
@@ -310,6 +352,7 @@
|
||||
"interface_mode": "composite",
|
||||
"interface_reason": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"is_alias": false,
|
||||
"metadata_source": "corecmd.contract",
|
||||
"name": "url_get",
|
||||
"parameter_count": 0,
|
||||
"parameters": {},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+3460
-3171
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+14239
-13622
File diff suppressed because it is too large
Load Diff
+5446
-4917
File diff suppressed because it is too large
Load Diff
+2441
-2412
File diff suppressed because it is too large
Load Diff
@@ -119,7 +119,14 @@ func TestEmbeddedCatalogPreservesRegistryIdentityAndManualParameterContract(t *t
|
||||
t.Fatalf("source = %q, want reviewed_command_registry", got)
|
||||
}
|
||||
identity := schemaMap(leaf["field_provenance"])["canonical_path"]
|
||||
if identity["source"] != "reviewed_command_registry" || identity["precedence"] != "command_registry" {
|
||||
// Migrated ContractFinal tools stamp identity provenance as contract_final
|
||||
// pass-through while the registry remains the path index; accept either.
|
||||
switch identity["precedence"] {
|
||||
case "command_registry", "contract_final":
|
||||
default:
|
||||
t.Fatalf("canonical identity provenance = %#v", identity)
|
||||
}
|
||||
if identity["precedence"] == "command_registry" && identity["source"] != "reviewed_command_registry" {
|
||||
t.Fatalf("canonical identity provenance = %#v", identity)
|
||||
}
|
||||
parameters := schemaMap(leaf["parameters"])
|
||||
|
||||
@@ -159,6 +159,20 @@ func BindEffectiveCommandRegistry(root *cobra.Command, effective EffectiveComman
|
||||
for _, alias := range item.Aliases {
|
||||
bound.ByCLIPath[alias] = item
|
||||
}
|
||||
// Migrate reviewed hint facts onto undeclared leaves without replacing
|
||||
// their Execute/RunE bodies. Already-declared ContractFinal (e.g. LeafSpec
|
||||
// Schema) wins and is left untouched — execution stays the control variable.
|
||||
if !HasRuntimeContractFinal(item.PrimaryCommand) {
|
||||
if decl, ok := lookupSchemaHintDecl(item.CanonicalPath); ok {
|
||||
attachSchemaHintDecl(item.PrimaryCommand, decl)
|
||||
}
|
||||
}
|
||||
// Index Contract-declared dry_run capabilities at bind time: every
|
||||
// process that resolves the command tree gets the reviewed set, not
|
||||
// only processes that also run Schema assembly.
|
||||
if payload, ok := RuntimeContractFinal(item.PrimaryCommand); ok && payload.DryRun != nil {
|
||||
recordDeclaredDryRunCapability(item.CanonicalPath, *payload.DryRun)
|
||||
}
|
||||
}
|
||||
return bound, nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,50 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// TestBindEffectiveCommandRegistryIndexesDeclaredDryRun pins bind-time
|
||||
// capability indexing: any process resolving the command tree gets the
|
||||
// Contract-declared dry_run in the reviewed set, without running assembly.
|
||||
func TestBindEffectiveCommandRegistryIndexesDeclaredDryRun(t *testing.T) {
|
||||
clearDeclaredDryRunCapabilitiesForTest()
|
||||
t.Cleanup(clearDeclaredDryRunCapabilitiesForTest)
|
||||
|
||||
root := commandRegistryTestRoot("item get")
|
||||
leaf := exactSchemaCommand(root, "item get")
|
||||
RegisterRuntimeContractFinal(leaf, ContractFinalPayload{
|
||||
DryRun: &DryRunSpec{PreviewKind: DryRunPreviewInvocation},
|
||||
})
|
||||
t.Cleanup(func() { ClearRuntimeContractFinalForTest(leaf) })
|
||||
|
||||
reviewed := mustCommandRegistry(t, []CommandSpec{{
|
||||
CanonicalPath: "item.get_item",
|
||||
PrimaryCLIPath: "item get",
|
||||
}})
|
||||
manual := ManualSchemaHintSnapshot{
|
||||
Version: manualSchemaHintVersion,
|
||||
Commands: []ManualSchemaCommandHint{{
|
||||
CLIPath: "item get",
|
||||
CanonicalPath: "item.get_item",
|
||||
Reason: "Confirms the reviewed primary identity",
|
||||
Reviewed: true,
|
||||
}},
|
||||
}
|
||||
effective, err := buildEffectiveCommandRegistry(root, reviewed, manual)
|
||||
if err != nil {
|
||||
t.Fatalf("buildEffectiveCommandRegistry() error = %v", err)
|
||||
}
|
||||
if _, err := BindEffectiveCommandRegistry(root, effective); err != nil {
|
||||
t.Fatalf("BindEffectiveCommandRegistry() error = %v", err)
|
||||
}
|
||||
capabilities, err := loadReviewedDryRunCapabilities()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
spec, ok := capabilities["item.get_item"]
|
||||
if !ok || spec.PreviewKind != DryRunPreviewInvocation {
|
||||
t.Fatalf("declared dry-run capability after bind = %#v, ok=%v", spec, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildEffectiveCommandRegistryMergesReviewedManualCommands(t *testing.T) {
|
||||
root := commandRegistryTestRoot("item get", "item legacy", "helper add")
|
||||
annotateTestCompatibilityPair(exactSchemaCommand(root, "item get"), exactSchemaCommand(root, "item legacy"))
|
||||
|
||||
@@ -79,6 +79,36 @@
|
||||
"calendar acl add",
|
||||
"calendar acl delete",
|
||||
"calendar book update",
|
||||
"chat category add-conv",
|
||||
"chat category create",
|
||||
"chat category delete",
|
||||
"chat category remove-conv",
|
||||
"chat category rename",
|
||||
"chat chmod",
|
||||
"chat clear-all-red-point",
|
||||
"chat clear-messages",
|
||||
"chat clear-red-point",
|
||||
"chat data-auth cross-org",
|
||||
"chat group audit-join-validation",
|
||||
"chat group list-all",
|
||||
"chat group list-join-validations",
|
||||
"chat group members list-by-ids",
|
||||
"chat group notice create",
|
||||
"chat group notice edit",
|
||||
"chat group notice get",
|
||||
"chat group notice list",
|
||||
"chat group share-invite",
|
||||
"chat group update-alias",
|
||||
"chat hide",
|
||||
"chat list-all-conversations",
|
||||
"chat mark-read",
|
||||
"chat mark-unread",
|
||||
"chat message list-emotion-replies",
|
||||
"chat message set-top-msg",
|
||||
"chat message unset-top-msg",
|
||||
"chat mute-at-all",
|
||||
"chat mute-red-envelope",
|
||||
"chat text translate",
|
||||
"contact label get",
|
||||
"contact label list",
|
||||
"contact label list-members",
|
||||
|
||||
@@ -1,378 +1,6 @@
|
||||
{
|
||||
"id": "chat",
|
||||
"tools": [
|
||||
{
|
||||
"canonical_path": "chat.add_conv_to_categories",
|
||||
"cli_path": "chat category add-conv"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_custom_group_role",
|
||||
"cli_path": "chat group-role add"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_emoji_reaction",
|
||||
"cli_path": "chat message add-emoji"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_group_member",
|
||||
"cli_path": "chat group members add"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_message_favorite",
|
||||
"cli_path": "chat message add-favorite"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_robot_to_group",
|
||||
"cli_path": "chat group members add-bot"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_text_emotion",
|
||||
"cli_path": "chat message add-text-emotion"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.audit_join_group",
|
||||
"cli_path": "chat group audit-join-validation"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.batch_query_group_chat_settings",
|
||||
"cli_path": "chat group user-settings query"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.batch_update_group_chat_settings",
|
||||
"cli_path": "chat group user-settings set"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.clear_all_red_point",
|
||||
"cli_path": "chat clear-all-red-point"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.clear_conversation_messages",
|
||||
"cli_path": "chat clear-messages"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.clear_conversation_red_point",
|
||||
"cli_path": "chat clear-red-point"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.combine_forward_messages",
|
||||
"cli_path": "chat message combine-forward"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_and_send_card",
|
||||
"cli_path": "chat message send-card"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_conv_category",
|
||||
"cli_path": "chat category create"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_group_conversation",
|
||||
"cli_path": "chat group create"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_group_notice",
|
||||
"cli_path": "chat group notice create"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_smart_conv_category",
|
||||
"cli_path": "chat category create-smart"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_text_emotion",
|
||||
"cli_path": "chat message create-text-emotion"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.delete_conv_category",
|
||||
"cli_path": "chat category delete"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.dismiss_group",
|
||||
"cli_path": "chat group dismiss"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.download_media",
|
||||
"cli_path": "chat message download-media"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.edit_group_notice",
|
||||
"cli_path": "chat group notice edit"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.edit_message",
|
||||
"cli_path": "chat message edit"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.forward_message",
|
||||
"cli_path": "chat message forward"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.forward_topic",
|
||||
"cli_path": "chat message forward-topic"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_conv_categories_info",
|
||||
"cli_path": "chat category batch-info"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_conv_info_by_group_id",
|
||||
"cli_path": "chat group get-by-group-id"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_conversation_info",
|
||||
"cli_path": "chat conversation-info"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_group_invite_url",
|
||||
"cli_path": "chat group invite-url"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_group_mute_config",
|
||||
"cli_path": "chat group get-mute-config"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_group_notice",
|
||||
"cli_path": "chat group notice get"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.grant_cross_org_data_access",
|
||||
"cli_path": "chat data-auth cross-org"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.grant_permission",
|
||||
"cli_path": "chat chmod"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.hide_conversation",
|
||||
"cli_path": "chat hide"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_all_conversations",
|
||||
"cli_path": "chat list-all-conversations"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_apply_join_group_records",
|
||||
"cli_path": "chat group list-join-validations"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_conv_categories_by_conv",
|
||||
"cli_path": "chat category list-by-conv"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_conversation_message_v2",
|
||||
"cli_path": "chat message list"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_conversations_by_category",
|
||||
"cli_path": "chat category list-conversations"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_custom_group_roles",
|
||||
"cli_path": "chat group-role list"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_group_bots",
|
||||
"cli_path": "chat group bots"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_group_member_by_ids",
|
||||
"cli_path": "chat group members list-by-ids"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_group_notices",
|
||||
"cli_path": "chat group notice list"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_individual_chat_message",
|
||||
"cli_path": "chat message list-direct"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_message_emotion_replies",
|
||||
"cli_path": "chat message list-emotion-replies"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_message_favorites",
|
||||
"cli_path": "chat message list-favorites"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_messages_by_ids",
|
||||
"cli_path": "chat message list-by-ids"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_my_groups_pagination",
|
||||
"cli_path": "chat group list-all"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_owned_or_admin_groups",
|
||||
"cli_path": "chat group list-my-groups"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_pin_messages",
|
||||
"cli_path": "chat message list-pin-msg"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_special_focus_messages",
|
||||
"cli_path": "chat message list-focused"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_top_conversations",
|
||||
"cli_path": "chat list-top-conversations"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_topic_replies",
|
||||
"cli_path": "chat message list-topic-replies"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_user_define_conv_categories",
|
||||
"cli_path": "chat category list"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.mark_conversation_unread",
|
||||
"cli_path": "chat mark-unread"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.mark_message_read",
|
||||
"cli_path": "chat mark-read"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.query_custom_user_roles",
|
||||
"cli_path": "chat group-role query-user"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.query_message_send_status",
|
||||
"cli_path": "chat message query-send-status"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.query_msg_read_status",
|
||||
"cli_path": "chat message read-status"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.quit_group",
|
||||
"cli_path": "chat group quit"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.recall_message",
|
||||
"cli_path": "chat message recall"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.recall_robot_message",
|
||||
"cli_path": "chat message recall-by-bot"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_conv_from_categories",
|
||||
"cli_path": "chat category remove-conv"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_custom_group_role",
|
||||
"cli_path": "chat group-role remove"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_custom_user_roles",
|
||||
"cli_path": "chat group-role remove-user"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_emoji_reaction",
|
||||
"cli_path": "chat message remove-emoji"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_group_member",
|
||||
"cli_path": "chat group members remove"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_message_favorite",
|
||||
"cli_path": "chat message remove-favorite"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_robot_in_group",
|
||||
"cli_path": "chat group members remove-bot"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_text_emotion",
|
||||
"cli_path": "chat message remove-text-emotion"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.rename_conv_category",
|
||||
"cli_path": "chat category rename"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.reply_personal_message",
|
||||
"cli_path": "chat message reply"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_at_me_message",
|
||||
"cli_path": "chat message list-mentions"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_bots",
|
||||
"cli_path": "chat bot find"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_common_groups",
|
||||
"cli_path": "chat search-common"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_groups",
|
||||
"cli_path": "chat search"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_messages",
|
||||
"cli_path": "chat message search-advanced"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_messages_by_keyword",
|
||||
"cli_path": "chat message search"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_messages_by_sender",
|
||||
"cli_path": "chat message list-by-sender"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_messages_by_time_range",
|
||||
"cli_path": "chat message list-all"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_my_robots",
|
||||
"cli_path": "chat bot search"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.send_message_by_custom_robot",
|
||||
"cli_path": "chat message send-by-webhook"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.send_personal_message",
|
||||
"cli_path": "chat message send"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.send_robot_message",
|
||||
"cli_path": "chat message send-by-bot"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_custom_user_roles",
|
||||
"cli_path": "chat group-role set-user"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_group_member_mute_list",
|
||||
"cli_path": "chat group-mute-member"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_group_mute",
|
||||
"cli_path": "chat group-mute"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_pin_message",
|
||||
"cli_path": "chat message set-pin-msg"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_top_conversation",
|
||||
"cli_path": "chat set-top"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_top_message",
|
||||
"cli_path": "chat message set-top-msg"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.share_group_invite_url",
|
||||
"cli_path": "chat group share-invite"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_at_me",
|
||||
"cli_path": "chat +at-me"
|
||||
@@ -429,10 +57,6 @@
|
||||
"canonical_path": "chat.shortcut_chat_list_mine",
|
||||
"cli_path": "chat +chat-list-mine"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_chat_messages",
|
||||
"cli_path": "chat +chat-messages"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_chat_mute",
|
||||
"cli_path": "chat +chat-mute"
|
||||
@@ -525,18 +149,10 @@
|
||||
"canonical_path": "chat.shortcut_messages_read_status",
|
||||
"cli_path": "chat +messages-read-status"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_messages_send",
|
||||
"cli_path": "chat +messages-send"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_messages_send_by_webhook",
|
||||
"cli_path": "chat +messages-send-by-webhook"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_messages_send_card",
|
||||
"cli_path": "chat +messages-send-card"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_messages_update_card",
|
||||
"cli_path": "chat +messages-update-card"
|
||||
@@ -545,62 +161,114 @@
|
||||
"canonical_path": "chat.shortcut_my_groups",
|
||||
"cli_path": "chat +my-groups"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_search_msg",
|
||||
"cli_path": "chat +search-msg"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_send_to_group",
|
||||
"cli_path": "chat +send-to-group"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_thread_replies",
|
||||
"cli_path": "chat +thread-replies"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_unread_chats",
|
||||
"cli_path": "chat +unread-chats"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.transfer_group_owner",
|
||||
"cli_path": "chat group transfer-owner"
|
||||
"canonical_path": "chat.search_bots",
|
||||
"cli_path": "chat bot find"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.translate",
|
||||
"cli_path": "chat text translate"
|
||||
"canonical_path": "chat.search_my_robots",
|
||||
"cli_path": "chat bot search"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.unread_message_conversation_list",
|
||||
"cli_path": "chat message list-unread-conversations"
|
||||
"canonical_path": "chat.get_conv_categories_info",
|
||||
"cli_path": "chat category batch-info"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.unset_pin_message",
|
||||
"cli_path": "chat message unset-pin-msg"
|
||||
"canonical_path": "chat.create_smart_conv_category",
|
||||
"cli_path": "chat category create-smart"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.unset_top_message",
|
||||
"cli_path": "chat message unset-top-msg"
|
||||
"canonical_path": "chat.list_user_define_conv_categories",
|
||||
"cli_path": "chat category list"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_at_all_notification_off",
|
||||
"cli_path": "chat mute-at-all"
|
||||
"canonical_path": "chat.list_conv_categories_by_conv",
|
||||
"cli_path": "chat category list-by-conv"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_conversations_by_category",
|
||||
"cli_path": "chat category list-conversations"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_conversation_info",
|
||||
"cli_path": "chat conversation-info"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_group_bots",
|
||||
"cli_path": "chat group bots"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_group_conversation",
|
||||
"cli_path": "chat group create"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.dismiss_group",
|
||||
"cli_path": "chat group dismiss"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_conv_info_by_group_id",
|
||||
"cli_path": "chat group get-by-group-id"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_group_mute_config",
|
||||
"cli_path": "chat group get-mute-config"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.get_group_invite_url",
|
||||
"cli_path": "chat group invite-url"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_owned_or_admin_groups",
|
||||
"cli_path": "chat group list-my-groups"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_group_member",
|
||||
"cli_path": "chat group members add"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_robot_to_group",
|
||||
"cli_path": "chat group members add-bot"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_group_member",
|
||||
"cli_path": "chat group members remove"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_robot_in_group",
|
||||
"cli_path": "chat group members remove-bot"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.quit_group",
|
||||
"cli_path": "chat group quit"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_group_name",
|
||||
"cli_path": "chat group rename"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_conv_member_roles",
|
||||
"cli_path": "chat group set-admin"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_custom_group_role",
|
||||
"cli_path": "chat group-role update"
|
||||
"canonical_path": "chat.update_show_history_msg_option",
|
||||
"cli_path": "chat group set-history"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.transfer_group_owner",
|
||||
"cli_path": "chat group transfer-owner"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_group_icon",
|
||||
"cli_path": "chat group update-icon"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_group_name",
|
||||
"cli_path": "chat group rename"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_group_nick",
|
||||
"cli_path": "chat group update-nick"
|
||||
@@ -610,16 +278,200 @@
|
||||
"cli_path": "chat group update-settings"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_notification_off",
|
||||
"cli_path": "chat mute"
|
||||
"canonical_path": "chat.upgrade_group_to_external",
|
||||
"cli_path": "chat group upgrade-to-external"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_red_env_notification_off",
|
||||
"cli_path": "chat mute-red-envelope"
|
||||
"canonical_path": "chat.batch_query_group_chat_settings",
|
||||
"cli_path": "chat group user-settings query"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_show_history_msg_option",
|
||||
"cli_path": "chat group set-history"
|
||||
"canonical_path": "chat.batch_update_group_chat_settings",
|
||||
"cli_path": "chat group user-settings set"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_group_mute",
|
||||
"cli_path": "chat group-mute"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_group_member_mute_list",
|
||||
"cli_path": "chat group-mute-member"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_custom_group_role",
|
||||
"cli_path": "chat group-role add"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_custom_group_roles",
|
||||
"cli_path": "chat group-role list"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.query_custom_user_roles",
|
||||
"cli_path": "chat group-role query-user"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_custom_group_role",
|
||||
"cli_path": "chat group-role remove"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_custom_user_roles",
|
||||
"cli_path": "chat group-role remove-user"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_custom_user_roles",
|
||||
"cli_path": "chat group-role set-user"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_custom_group_role",
|
||||
"cli_path": "chat group-role update"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_top_conversations",
|
||||
"cli_path": "chat list-top-conversations"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_emoji_reaction",
|
||||
"cli_path": "chat message add-emoji"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_message_favorite",
|
||||
"cli_path": "chat message add-favorite"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.add_text_emotion",
|
||||
"cli_path": "chat message add-text-emotion"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.combine_forward_messages",
|
||||
"cli_path": "chat message combine-forward"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_text_emotion",
|
||||
"cli_path": "chat message create-text-emotion"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.download_media",
|
||||
"cli_path": "chat message download-media"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.edit_message",
|
||||
"cli_path": "chat message edit"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.forward_message",
|
||||
"cli_path": "chat message forward"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.forward_topic",
|
||||
"cli_path": "chat message forward-topic"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_conversation_message_v2",
|
||||
"cli_path": "chat message list"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_messages_by_time_range",
|
||||
"cli_path": "chat message list-all"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_messages_by_ids",
|
||||
"cli_path": "chat message list-by-ids"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_messages_by_sender",
|
||||
"cli_path": "chat message list-by-sender"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_individual_chat_message",
|
||||
"cli_path": "chat message list-direct"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_message_favorites",
|
||||
"cli_path": "chat message list-favorites"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_special_focus_messages",
|
||||
"cli_path": "chat message list-focused"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_at_me_message",
|
||||
"cli_path": "chat message list-mentions"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_pin_messages",
|
||||
"cli_path": "chat message list-pin-msg"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.list_topic_replies",
|
||||
"cli_path": "chat message list-topic-replies"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.unread_message_conversation_list",
|
||||
"cli_path": "chat message list-unread-conversations"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.query_message_send_status",
|
||||
"cli_path": "chat message query-send-status"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.query_msg_read_status",
|
||||
"cli_path": "chat message read-status"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.recall_message",
|
||||
"cli_path": "chat message recall"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.recall_robot_message",
|
||||
"cli_path": "chat message recall-by-bot"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_emoji_reaction",
|
||||
"cli_path": "chat message remove-emoji"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_message_favorite",
|
||||
"cli_path": "chat message remove-favorite"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.remove_text_emotion",
|
||||
"cli_path": "chat message remove-text-emotion"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.reply_personal_message",
|
||||
"cli_path": "chat message reply"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_messages_by_keyword",
|
||||
"cli_path": "chat message search"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_messages",
|
||||
"cli_path": "chat message search-advanced"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.send_personal_message",
|
||||
"cli_path": "chat message send"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.send_robot_message",
|
||||
"cli_path": "chat message send-by-bot"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.send_message_by_custom_robot",
|
||||
"cli_path": "chat message send-by-webhook"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.create_and_send_card",
|
||||
"cli_path": "chat message send-card"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_pin_message",
|
||||
"cli_path": "chat message set-pin-msg"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.unset_pin_message",
|
||||
"cli_path": "chat message unset-pin-msg"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_streaming_card",
|
||||
@@ -630,12 +482,40 @@
|
||||
"cli_path": "chat message update-text-emotion"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.update_user_group_alias",
|
||||
"cli_path": "chat group update-alias"
|
||||
"canonical_path": "chat.update_notification_off",
|
||||
"cli_path": "chat mute"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.upgrade_group_to_external",
|
||||
"cli_path": "chat group upgrade-to-external"
|
||||
"canonical_path": "chat.search_groups",
|
||||
"cli_path": "chat search"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.search_common_groups",
|
||||
"cli_path": "chat search-common"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.set_top_conversation",
|
||||
"cli_path": "chat set-top"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_chat_messages",
|
||||
"cli_path": "chat +chat-messages"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_messages_send",
|
||||
"cli_path": "chat +messages-send"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_messages_send_card",
|
||||
"cli_path": "chat +messages-send-card"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_search_msg",
|
||||
"cli_path": "chat +search-msg"
|
||||
},
|
||||
{
|
||||
"canonical_path": "chat.shortcut_thread_replies",
|
||||
"cli_path": "chat +thread-replies"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -260,9 +260,12 @@ type SelectionSpec struct {
|
||||
Tips []string
|
||||
WorkflowRefs []string
|
||||
Examples []string
|
||||
Reviewed *bool
|
||||
SourceRefs []string
|
||||
MetadataSource string
|
||||
// Reviewed is a legacy-path (hints/registry) marker only. The Contract
|
||||
// declaration path must not set it: declared selection is final by
|
||||
// construction, and assembly rejects a declared payload carrying it.
|
||||
Reviewed *bool
|
||||
SourceRefs []string
|
||||
MetadataSource string
|
||||
}
|
||||
|
||||
// FieldProvenance records how one final field was selected. Value is raw JSON
|
||||
|
||||
@@ -17,36 +17,17 @@ type dryRunCapabilityGroup struct {
|
||||
CanonicalPaths []string
|
||||
}
|
||||
|
||||
// reviewedDryRunCapabilityGroups contains only command-owned preview paths.
|
||||
// reviewedDryRunCapabilityGroups contains only command-owned preview paths
|
||||
// for tools WITHOUT a Contract final declaration. Declared tools publish
|
||||
// their dry_run capability from corecmd.SchemaDecl (reviewed code) and are
|
||||
// merged into the reviewed set at assembly time — no manual list entry.
|
||||
// Inheriting the root --dry-run flag or reaching the generic EchoRunner is not
|
||||
// evidence of a stable capability and must never add a command to this list.
|
||||
// CI executes each selected example and compares the observed preview kind to
|
||||
// this reviewed declaration.
|
||||
var reviewedDryRunCapabilityGroups = []dryRunCapabilityGroup{
|
||||
{PreviewKind: DryRunPreviewRequest, CanonicalPaths: []string{
|
||||
"event.stop",
|
||||
}},
|
||||
{PreviewKind: DryRunPreviewPlan, CanonicalPaths: []string{
|
||||
"chat.download_media",
|
||||
"doc.download_file",
|
||||
"doc.import_get",
|
||||
"doc.media_insert",
|
||||
"doc.query_export_job",
|
||||
"doc.upload",
|
||||
"drive.download_file",
|
||||
"drive.upload",
|
||||
"markdown.create",
|
||||
"markdown.fetch",
|
||||
"markdown.overwrite",
|
||||
"markdown.patch",
|
||||
"sheet.filter_view_get_criteria",
|
||||
"sheet.filter_view_info",
|
||||
"sheet.filter_view_list_criteria",
|
||||
"sheet.media_upload",
|
||||
"sheet.submit_export_job",
|
||||
"sheet.write_image",
|
||||
"todo.add_todo_attachment",
|
||||
}},
|
||||
// Declared tools publish dry_run from schemaHintDecls / LeafSpec Schema.
|
||||
// Manual entries remain only for tools that cannot yet declare ContractFinal.
|
||||
}
|
||||
|
||||
var reviewedDryRunCapabilitiesLazy struct {
|
||||
@@ -55,7 +36,34 @@ var reviewedDryRunCapabilitiesLazy struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func loadReviewedDryRunCapabilities() (map[string]DryRunSpec, error) {
|
||||
// declaredDryRunCapabilities indexes dry_run capabilities sourced from
|
||||
// Contract final declarations (canonical → spec). Populated by
|
||||
// BindEffectiveCommandRegistry at command-tree bind time — every process
|
||||
// that resolves the tree gets the reviewed set, not only processes that run
|
||||
// Schema assembly. A declaration in reviewed code is itself the reviewed
|
||||
// capability, so no manual allowlist entry is required.
|
||||
var declaredDryRunCapabilities sync.Map // string → DryRunSpec
|
||||
|
||||
// recordDeclaredDryRunCapability registers one Contract-declared dry_run
|
||||
// capability. Conflicting re-declaration of the same canonical is a
|
||||
// programming error surfaced at the next delivery gate read.
|
||||
func recordDeclaredDryRunCapability(canonical string, spec DryRunSpec) {
|
||||
canonical = strings.TrimSpace(canonical)
|
||||
if canonical == "" {
|
||||
return
|
||||
}
|
||||
declaredDryRunCapabilities.Store(canonical, spec)
|
||||
}
|
||||
|
||||
// clearDeclaredDryRunCapabilitiesForTest resets the declared index (tests only).
|
||||
func clearDeclaredDryRunCapabilitiesForTest() {
|
||||
declaredDryRunCapabilities.Range(func(key, _ any) bool {
|
||||
declaredDryRunCapabilities.Delete(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func loadManualDryRunCapabilities() (map[string]DryRunSpec, error) {
|
||||
reviewedDryRunCapabilitiesLazy.once.Do(func() {
|
||||
byCanonical := make(map[string]DryRunSpec)
|
||||
for _, group := range reviewedDryRunCapabilityGroups {
|
||||
@@ -95,6 +103,36 @@ func loadReviewedDryRunCapabilities() (map[string]DryRunSpec, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadReviewedDryRunCapabilities() (map[string]DryRunSpec, error) {
|
||||
out, err := loadManualDryRunCapabilities()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mergeErr error
|
||||
declaredDryRunCapabilities.Range(func(key, value any) bool {
|
||||
canonical, ok := key.(string)
|
||||
if !ok {
|
||||
mergeErr = fmt.Errorf("declared dry-run capability has non-string key %v", key)
|
||||
return false
|
||||
}
|
||||
spec, ok := value.(DryRunSpec)
|
||||
if !ok {
|
||||
mergeErr = fmt.Errorf("declared dry-run capability %s has non-DryRunSpec value", canonical)
|
||||
return false
|
||||
}
|
||||
if manual, exists := out[canonical]; exists && manual != spec {
|
||||
mergeErr = fmt.Errorf("dry-run capability %s declared as %#v conflicts with manual reviewed entry %#v", canonical, spec, manual)
|
||||
return false
|
||||
}
|
||||
out[canonical] = spec
|
||||
return true
|
||||
})
|
||||
if mergeErr != nil {
|
||||
return nil, mergeErr
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ReviewedDryRunCapabilities returns a defensive copy of the positive,
|
||||
// reviewed capability registry for delivery gates.
|
||||
func ReviewedDryRunCapabilities() (map[string]DryRunSpec, error) {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// schemaHintDecl is one command's Safety + Schema migrated from reviewed
|
||||
// schema_hints selection/metadata into compiled Go.
|
||||
type schemaHintDecl struct {
|
||||
Safety SafetySpec
|
||||
Description string
|
||||
DryRun *DryRunSpec
|
||||
Interface *InterfaceSpec
|
||||
Selection SelectionSpec
|
||||
}
|
||||
|
||||
// lookupSchemaHintDecl returns the migrated declaration for a canonical path.
|
||||
func lookupSchemaHintDecl(canonical string) (schemaHintDecl, bool) {
|
||||
canonical = strings.TrimSpace(canonical)
|
||||
if canonical == "" {
|
||||
return schemaHintDecl{}, false
|
||||
}
|
||||
d, ok := schemaHintDeclsByCanonical[canonical]
|
||||
return d, ok
|
||||
}
|
||||
|
||||
// attachSchemaHintDecl registers ContractFinal from a migrated hint declaration
|
||||
// without replacing RunE/Execute. Completeness is enforced here so bind-time
|
||||
// attaches cannot ship a partial overlay.
|
||||
func attachSchemaHintDecl(cmd *cobra.Command, decl schemaHintDecl) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(decl.Description) == "" {
|
||||
panic(fmt.Sprintf("schema hint decl for %s missing Description", cmd.CommandPath()))
|
||||
}
|
||||
if strings.TrimSpace(decl.Selection.AgentSummary) == "" ||
|
||||
len(decl.Selection.UseWhen) == 0 || len(decl.Selection.AvoidWhen) == 0 ||
|
||||
len(decl.Selection.Examples) == 0 {
|
||||
panic(fmt.Sprintf("schema hint decl for %s missing Selection prose", cmd.CommandPath()))
|
||||
}
|
||||
if decl.Interface == nil || strings.TrimSpace(decl.Interface.Mode) == "" ||
|
||||
strings.TrimSpace(decl.Interface.Availability) == "" {
|
||||
panic(fmt.Sprintf("schema hint decl for %s missing Interface", cmd.CommandPath()))
|
||||
}
|
||||
if (decl.Interface.Mode == InterfaceModeComposite ||
|
||||
decl.Interface.Availability == InterfaceUnavailable) &&
|
||||
strings.TrimSpace(decl.Interface.Reason) == "" {
|
||||
panic(fmt.Sprintf("schema hint decl for %s missing Interface.Reason", cmd.CommandPath()))
|
||||
}
|
||||
|
||||
safety := decl.Safety
|
||||
if strings.TrimSpace(safety.Effect) == "" && strings.TrimSpace(safety.Risk) == "" &&
|
||||
strings.TrimSpace(safety.Confirmation) == "" && strings.TrimSpace(safety.Idempotency) == "" {
|
||||
safety = SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
}
|
||||
}
|
||||
safety.EffectSource = "corecmd.contract"
|
||||
|
||||
payload := ContractFinalPayload{
|
||||
Title: firstNonEmptyTrim(cmd.Short),
|
||||
Description: firstNonEmptyTrim(decl.Description, cmd.Long),
|
||||
Safety: &safety,
|
||||
DryRun: decl.DryRun,
|
||||
Interface: decl.Interface,
|
||||
Selection: &SelectionSpec{
|
||||
AgentSummary: strings.TrimSpace(decl.Selection.AgentSummary),
|
||||
UseWhen: decl.Selection.UseWhen,
|
||||
AvoidWhen: decl.Selection.AvoidWhen,
|
||||
Prerequisites: decl.Selection.Prerequisites,
|
||||
Tips: decl.Selection.Tips,
|
||||
WorkflowRefs: decl.Selection.WorkflowRefs,
|
||||
Examples: decl.Selection.Examples,
|
||||
},
|
||||
}
|
||||
RegisterRuntimeContractFinal(cmd, payload)
|
||||
}
|
||||
|
||||
func firstNonEmptyTrim(values ...string) string {
|
||||
for _, v := range values {
|
||||
if s := strings.TrimSpace(v); s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
//
|
||||
// Intentionally empty after the ContractFinal migration: leaf and shortcut
|
||||
// declarations now live in helpers.DeclareLeafMetadata / Shortcut.Schema, so the
|
||||
// lookup in schema_hint_decls.go always misses and callers fall through to the
|
||||
// declaration path. No generator maintains this table.
|
||||
|
||||
package cli
|
||||
|
||||
var schemaHintDeclsByCanonical = map[string]schemaHintDecl{}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func testSchemaHintDeclFixture() schemaHintDecl {
|
||||
return schemaHintDecl{
|
||||
Safety: SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Description: "fixture command for attachSchemaHintDecl freeze test",
|
||||
Interface: &InterfaceSpec{
|
||||
Mode: InterfaceModeMCP, Availability: InterfaceAvailable,
|
||||
Ref: &InterfaceRefSpec{ProductID: "fixture", RPCName: "fixture_probe"},
|
||||
},
|
||||
Selection: SelectionSpec{
|
||||
AgentSummary: "fixture probe",
|
||||
UseWhen: []string{"unit testing attachSchemaHintDecl"},
|
||||
AvoidWhen: []string{"production use"},
|
||||
Examples: []string{"dws fixture probe"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchemaHintDeclAttachDoesNotReplaceRunE(t *testing.T) {
|
||||
ran := false
|
||||
cmd := &cobra.Command{
|
||||
Use: "probe",
|
||||
Short: "probe",
|
||||
RunE: func(*cobra.Command, []string) error {
|
||||
ran = true
|
||||
return nil
|
||||
},
|
||||
}
|
||||
attachSchemaHintDecl(cmd, testSchemaHintDeclFixture())
|
||||
if !HasRuntimeContractFinal(cmd) {
|
||||
t.Fatal("expected ContractFinal after attach")
|
||||
}
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("Execute() error = %v", err)
|
||||
}
|
||||
if !ran {
|
||||
t.Fatal("RunE body was not invoked — execution must stay frozen")
|
||||
}
|
||||
ClearRuntimeContractFinalForTest(cmd)
|
||||
}
|
||||
|
||||
func TestSchemaHintDeclLookupCoverage(t *testing.T) {
|
||||
// All reviewed tools now author ContractFinal beside the command
|
||||
// (DeclareLeafMetadata / Shortcut.Schema). The compiled residual map is empty.
|
||||
if got := len(schemaHintDeclsByCanonical); got != 0 {
|
||||
t.Fatalf("compiled residual decls = %d, want 0 after full colocation", got)
|
||||
}
|
||||
if _, ok := lookupSchemaHintDecl("aitable.view_update_aggregate"); ok {
|
||||
t.Fatal("aitable.view_update_aggregate must not remain in compiled hint decls")
|
||||
}
|
||||
if _, ok := lookupSchemaHintDecl("aitable.shortcut_base_list"); ok {
|
||||
t.Fatal("aitable.shortcut_base_list must not remain in compiled hint decls")
|
||||
}
|
||||
if _, ok := lookupSchemaHintDecl("doc.create_document"); ok {
|
||||
t.Fatal("doc.create_document must not remain in compiled hint decls")
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,7 @@
|
||||
"reviewed": true
|
||||
},
|
||||
"tools": {
|
||||
"aisearch.enterprise_person_search": {
|
||||
"interface_mode": "mcp",
|
||||
"availability": "available",
|
||||
"reviewed": true
|
||||
},
|
||||
"aisearch.search_enterprise": {
|
||||
"interface_mode": "mcp",
|
||||
"availability": "available",
|
||||
"reviewed": true,
|
||||
"parameters": {
|
||||
"queries": {
|
||||
"required": false
|
||||
@@ -25,15 +17,9 @@
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input.",
|
||||
"reviewed": true,
|
||||
"cli_path": "aisearch enterprise",
|
||||
"runtime_gate": "none",
|
||||
"confirmation": "not_required"
|
||||
},
|
||||
"aisearch.search_enterprise_behavior": {
|
||||
"interface_mode": "mcp",
|
||||
"availability": "available",
|
||||
"reviewed": true
|
||||
"review_reason": "The reviewed CLI supports an all-content search with default --types all, while --queries and --time-range are optional filters; imported MCP required markers must not turn them into mandatory CLI input."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user