Files

683 lines
25 KiB
Go

// 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 (
"fmt"
"sort"
"strings"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contractfinal"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// AgentExampleMode controls only how an already contract-validated example is
// exercised. Contract validation is the default. dry_run is used only when the
// final ToolSpec publishes an explicit reviewed capability; contract_only
// remains a precise reviewed exception for such a capability whose runtime
// preconditions cannot be exercised safely and deterministically in the
// isolated test process.
type AgentExampleMode = contract.ExampleDispositionMode
const (
AgentExampleModeContract = contract.ExampleDispositionModeContract
AgentExampleModeDryRun = contract.ExampleDispositionModeDryRun
AgentExampleModeContractOnly = contract.ExampleDispositionModeContractOnly
)
// AgentExampleReasonCode is a closed taxonomy for reviewed contract-only
// exceptions to an explicit dry-run capability.
type AgentExampleReasonCode = contract.ExampleDispositionReasonCode
const (
AgentExampleReasonLocalState = contract.ExampleDispositionReasonLocalState
AgentExampleReasonStatefulPreflight = contract.ExampleDispositionReasonStatefulPreflight
)
// AgentExampleDisposition narrows one exact example with an explicit
// typed dry-run capability to contract-only. Index is a pointer so a missing
// field cannot silently select example zero.
//
// Dispositions are authored on the owning ContractFinal Selection.
type AgentExampleDisposition = contract.ExampleDisposition
// AgentExampleExecution is one resolved example and its effective test mode.
type AgentExampleExecution struct {
CanonicalPath string
Index int
Example string
Mode AgentExampleMode
DryRun *contract.DryRunSpec
ReasonCode AgentExampleReasonCode
Reason string
Source AgentExampleDispositionSource
}
// AgentExampleDispositionSource distinguishes the normal typed-contract
// classification from narrow reviewed exceptions.
type AgentExampleDispositionSource string
const (
AgentExampleDispositionDefault AgentExampleDispositionSource = "default"
AgentExampleDispositionReviewed AgentExampleDispositionSource = ProvenanceReviewedManual
)
// AgentExampleExecutionPlan is a stable, typed report used by the
// exhaustive real-Cobra dry-run test.
type AgentExampleExecutionPlan struct {
Examples []AgentExampleExecution
Total int
Contract int
DryRun int
ContractOnly int
ReviewedContractOnly int
ContractOnlyByReason map[AgentExampleReasonCode]int
}
// ValidateAgentExampleDelivery is the final generation gate. It validates every
// bound tool's ContractFinal Selection.Examples against the assembled typed
// SchemaRegistry.
func ValidateAgentExampleDelivery(bound BoundCommandRegistry, registry SchemaRegistry) (AgentExampleExecutionPlan, error) {
return BuildAgentExampleExecutionPlan(bound, registry)
}
// agentExampleSelectionFn is the selection source for example planning.
// Tests may override it to inject ExampleDispositions without changing
// ContractFinal production wiring.
var agentExampleSelectionFn = contractFinalToolSelection
// BuildAgentExampleExecutionPlan validates every ContractFinal example against
// its real BoundCommand/Cobra contract. Runtime dry-run execution is opt-in and
// comes only from the final typed ToolSpec.
func BuildAgentExampleExecutionPlan(bound BoundCommandRegistry, registry SchemaRegistry) (AgentExampleExecutionPlan, error) {
tools := make(map[string]ToolSpec, len(bound.Commands))
for _, product := range registry.Products {
for _, tool := range product.Tools {
canonical := strings.TrimSpace(tool.Identity.CanonicalPath)
if canonical == "" {
return AgentExampleExecutionPlan{}, fmt.Errorf("typed SchemaRegistry contains a tool with empty canonical path")
}
if _, duplicate := tools[canonical]; duplicate {
return AgentExampleExecutionPlan{}, fmt.Errorf("typed SchemaRegistry contains duplicate tool %q", canonical)
}
tools[canonical] = tool
}
}
return buildAgentExampleExecutionPlan(bound, tools)
}
// FixtureAgentSelectionSet is a transitional Agent selection fixture type
// retained only so live-selection call sites can project candidate tables.
// Production paths must keep it empty; example planning reads ContractFinal.
type FixtureAgentSelectionSet struct {
Revisions map[string]FixtureAgentSelectionRevision `json:"revisions,omitempty"`
Products map[string]FixtureAgentProductSelection `json:"products,omitempty"`
Tools map[string]AgentToolSelection `json:"tools,omitempty"`
}
// FixtureAgentSelectionRevision is retained for transitional fixtures only.
type FixtureAgentSelectionRevision struct {
GeneratedBy string `json:"generated_by"`
Model string `json:"model,omitempty"`
PromptVersion string `json:"prompt_version,omitempty"`
Reason string `json:"reason"`
}
// FixtureAgentProductSelection is retained for transitional fixtures only.
type FixtureAgentProductSelection struct {
AgentSummary string `json:"agent_summary"`
UseWhen []string `json:"use_when"`
AvoidWhen []string `json:"avoid_when"`
Reviewed bool `json:"reviewed"`
Revision string `json:"revision"`
Reason string `json:"reason"`
Evidence []string `json:"evidence"`
}
// AgentToolSelection is the tool-level selection projection used by example
// planning and live-selection fixtures. Production authority remains
// ContractFinal Selection on the owning leaf.
type AgentToolSelection struct {
AgentSummary string `json:"agent_summary"`
UseWhen []string `json:"use_when"`
AvoidWhen []string `json:"avoid_when"`
Examples []string `json:"examples"`
ExampleDispositions []AgentExampleDisposition `json:"example_dispositions,omitempty"`
Reviewed bool `json:"reviewed"`
Revision string `json:"revision"`
Reason string `json:"reason"`
Evidence []string `json:"evidence"`
}
func buildAgentExampleExecutionPlan(bound BoundCommandRegistry, typedTools map[string]ToolSpec) (AgentExampleExecutionPlan, error) {
plan := AgentExampleExecutionPlan{
ContractOnlyByReason: map[AgentExampleReasonCode]int{},
}
canonicalPaths := make([]string, 0, len(bound.Commands))
for _, command := range bound.Commands {
canonical := strings.TrimSpace(command.CanonicalPath)
if !contractfinal.HasRuntimeContractFinal(command.PrimaryCommand) {
return AgentExampleExecutionPlan{}, fmt.Errorf("bound tool %q has no ContractFinal declaration; Schema examples require leaf Schema.Selection", canonical)
}
canonicalPaths = append(canonicalPaths, canonical)
}
sort.Strings(canonicalPaths)
for _, canonical := range canonicalPaths {
spec, ok := bound.ByCanonical[canonical]
if !ok {
return AgentExampleExecutionPlan{}, fmt.Errorf("example plan references unknown canonical tool %q", canonical)
}
selection := agentExampleSelectionFn(spec.PrimaryCommand)
var typedTool ToolSpec
if typedTools != nil {
var found bool
typedTool, found = typedTools[canonical]
if !found {
return AgentExampleExecutionPlan{}, fmt.Errorf("example tool %q is missing from final typed SchemaRegistry", canonical)
}
}
if len(selection.Examples) == 0 {
return AgentExampleExecutionPlan{}, fmt.Errorf("ContractFinal tool %s requires non-empty Selection.Examples", canonical)
}
if len(selection.Examples) > 2 {
return AgentExampleExecutionPlan{}, fmt.Errorf("ContractFinal tool %s has %d examples; maximum is 2", canonical, len(selection.Examples))
}
if err := validateAgentExampleDispositions(canonical, selection.Examples, selection.ExampleDispositions); err != nil {
return AgentExampleExecutionPlan{}, err
}
dispositions := make(map[int]AgentExampleDisposition, len(selection.ExampleDispositions))
for _, disposition := range selection.ExampleDispositions {
dispositions[*disposition.Index] = disposition
}
paths := []agentExamplePath{{Path: spec.PrimaryCLIPath, Argv: strings.Fields(spec.PrimaryCLIPath), Command: spec.PrimaryCommand}}
for _, alias := range spec.AliasCommands {
paths = append(paths, agentExamplePath{Path: alias.Path, Argv: strings.Fields(alias.Path), Command: alias.Command})
}
sort.Slice(paths, func(i, j int) bool {
if len(paths[i].Argv) != len(paths[j].Argv) {
return len(paths[i].Argv) > len(paths[j].Argv)
}
return paths[i].Path < paths[j].Path
})
for index, example := range selection.Examples {
argv, err := tokenizeAgentExample(example)
if err != nil {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example has invalid argv syntax: %w", canonical, err)
}
if len(argv) < 2 || argv[0] != "dws" {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example must start with dws: %q", canonical, example)
}
for _, argument := range argv[1:] {
if argument == "--yes" || strings.HasPrefix(argument, "--yes=") {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example must not bypass confirmation with --yes", canonical)
}
if argument == "--help" || strings.HasPrefix(argument, "--help=") || argument == "-h" || strings.HasPrefix(argument, "-h=") {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example must demonstrate execution, not only --help", canonical)
}
}
remainder, matched, ok := matchAgentExamplePath(argv, paths)
if !ok {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example does not use its reviewed primary/alias path: %q", canonical, example)
}
if matched.Command == nil {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s reviewed path %q has no bound Cobra command", canonical, matched.Path)
}
constraints, err := strictCompatibilityConstraints(matched.Command, canonical)
if err != nil {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example for %q has invalid executable constraints: %w", canonical, matched.Path, err)
}
if typedTools != nil {
constraints.MutuallyExclusive = append(constraints.MutuallyExclusive, typedTool.Constraints.MutuallyExclusive...)
constraints.RequireOneOf = append(constraints.RequireOneOf, typedTool.Constraints.RequireOneOf...)
constraints.RequireTogether = append(constraints.RequireTogether, typedTool.Constraints.RequireTogether...)
constraints = normalizeRuntimeSchemaConstraints(constraints)
}
positionals, err := strictCompatibilityPositionals(matched.Command)
if err != nil {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example for %q has invalid executable positionals: %w", canonical, matched.Path, err)
}
if typedTools != nil {
positionals = mergeAgentExamplePositionals(positionals, typedTool.Positionals)
}
if err := validateAgentExampleCobraContract(matched.Command, remainder, constraints, positionals); err != nil {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example for %q: %w", canonical, matched.Path, err)
}
execution := AgentExampleExecution{
CanonicalPath: canonical,
Index: index,
Example: example,
Mode: AgentExampleModeContract,
Source: AgentExampleDispositionDefault,
}
if typedTool.DryRun != nil {
dryRun := *typedTool.DryRun
execution.DryRun = &dryRun
execution.Mode = AgentExampleModeDryRun
}
disposition, hasDisposition := dispositions[index]
if hasDisposition {
if typedTools != nil && execution.DryRun == nil {
return AgentExampleExecutionPlan{}, fmt.Errorf("tool %s example disposition index %d narrows no explicit dry_run capability", canonical, index)
}
execution.Mode = disposition.Mode
execution.ReasonCode = disposition.ReasonCode
execution.Reason = strings.TrimSpace(disposition.Reason)
execution.Source = AgentExampleDispositionReviewed
plan.ContractOnly++
plan.ReviewedContractOnly++
plan.ContractOnlyByReason[disposition.ReasonCode]++
} else if execution.DryRun != nil {
plan.DryRun++
} else {
plan.Contract++
}
plan.Examples = append(plan.Examples, execution)
plan.Total++
}
}
for canonical := range typedTools {
found := false
for _, command := range bound.Commands {
if strings.TrimSpace(command.CanonicalPath) == canonical {
found = true
break
}
}
if !found {
return AgentExampleExecutionPlan{}, fmt.Errorf("final typed SchemaRegistry tool %q is missing from BoundCommandRegistry", canonical)
}
}
return plan, nil
}
type agentExamplePath struct {
Path string
Argv []string
Command *cobra.Command
}
func matchAgentExamplePath(argv []string, paths []agentExamplePath) ([]string, agentExamplePath, bool) {
if len(argv) == 0 || argv[0] != "dws" {
return nil, agentExamplePath{}, false
}
for index := range paths {
if paths[index].Argv == nil {
paths[index].Argv = strings.Fields(strings.TrimSpace(paths[index].Path))
}
pathArgv := paths[index].Argv
if len(pathArgv) == 0 || len(argv) < len(pathArgv)+1 {
continue
}
matches := true
for offset := range pathArgv {
if argv[offset+1] != pathArgv[offset] {
matches = false
break
}
}
if matches {
return argv[len(pathArgv)+1:], paths[index], true
}
}
return nil, agentExamplePath{}, false
}
func validateAgentExampleDispositions(canonical string, examples []string, dispositions []AgentExampleDisposition) error {
seen := make(map[int]bool, len(dispositions))
for _, disposition := range dispositions {
if disposition.Index == nil {
return fmt.Errorf("tool %s example disposition requires index", canonical)
}
index := *disposition.Index
if index < 0 || index >= len(examples) {
return fmt.Errorf("tool %s example disposition index %d is out of range for %d examples", canonical, index, len(examples))
}
if seen[index] {
return fmt.Errorf("tool %s has duplicate example disposition index %d", canonical, index)
}
seen[index] = true
if !disposition.Reviewed {
return fmt.Errorf("tool %s example disposition index %d must be reviewed", canonical, index)
}
if disposition.Mode != AgentExampleModeContractOnly {
return fmt.Errorf("tool %s example disposition index %d has invalid mode %q; only %q may narrow an explicit dry_run capability", canonical, index, disposition.Mode, AgentExampleModeContractOnly)
}
if !validAgentExampleReasonCode(disposition.ReasonCode) {
return fmt.Errorf("tool %s example disposition index %d has invalid reason_code %q", canonical, index, disposition.ReasonCode)
}
if strings.TrimSpace(disposition.Reason) == "" {
return fmt.Errorf("tool %s example disposition index %d requires a non-empty reason", canonical, index)
}
}
return nil
}
func validAgentExampleReasonCode(code AgentExampleReasonCode) bool {
switch code {
case AgentExampleReasonLocalState, AgentExampleReasonStatefulPreflight:
return true
default:
return false
}
}
func tokenizeAgentExample(input string) ([]string, error) {
var (
argv []string
current strings.Builder
quote byte
tokenStarted bool
)
flush := func() {
if tokenStarted {
argv = append(argv, current.String())
current.Reset()
tokenStarted = false
}
}
for index := 0; index < len(input); index++ {
character := input[index]
if quote != 0 {
if character == quote {
quote = 0
continue
}
if quote == '"' && character == '\\' {
if index+1 >= len(input) {
return nil, fmt.Errorf("trailing escape in double-quoted value")
}
index++
current.WriteByte(input[index])
continue
}
if quote == '"' && (character == '`' || character == '$') {
return nil, fmt.Errorf("shell expansion is not allowed")
}
current.WriteByte(character)
continue
}
switch character {
case ' ', '\t':
flush()
case '\r', '\n':
return nil, fmt.Errorf("unquoted newline shell operator is not allowed")
case '\'', '"':
quote = character
tokenStarted = true
case '\\':
if index+1 >= len(input) {
return nil, fmt.Errorf("trailing escape")
}
index++
current.WriteByte(input[index])
tokenStarted = true
case '<':
placeholder, next, ok := agentExamplePlaceholderAt(input, index)
if !ok {
return nil, fmt.Errorf("shell redirection operator %q is not allowed", string(character))
}
current.WriteString(placeholder)
tokenStarted = true
index = next
case '>', ';', '|', '&', '(', ')':
return nil, fmt.Errorf("shell operator %q is not allowed", string(character))
case '`', '$':
return nil, fmt.Errorf("shell expansion is not allowed")
case '#':
return nil, fmt.Errorf("shell comments are not allowed")
default:
current.WriteByte(character)
tokenStarted = true
}
}
if quote != 0 {
return nil, fmt.Errorf("unterminated quoted value")
}
flush()
for _, argument := range argv {
if argument == "--" {
return nil, fmt.Errorf("the -- argument terminator is not allowed")
}
}
return argv, nil
}
// ParseAgentExampleArgv exposes the shell-free argv parser used by
// example validation and dry-run tests.
func ParseAgentExampleArgv(input string) ([]string, error) {
return tokenizeAgentExample(input)
}
func agentExamplePlaceholderAt(input string, start int) (string, int, bool) {
endOffset := strings.IndexByte(input[start+1:], '>')
if endOffset < 1 {
return "", start, false
}
end := start + 1 + endOffset
body := input[start+1 : end]
for index := 0; index < len(body); index++ {
character := body[index]
if strings.ContainsRune(" \t\r\n<>;&|`$()#'\"\\", rune(character)) {
return "", start, false
}
}
return input[start : end+1], end, true
}
func validateAgentExampleCobraContract(command *cobra.Command, arguments []string, constraints RuntimeSchemaConstraints, positionalSpecs []contract.RuntimeSchemaPositional) error {
if command == nil {
return fmt.Errorf("bound Cobra command is nil")
}
providedFacts := map[string]bool{}
positionals := make([]string, 0)
for index := 0; index < len(arguments); index++ {
argument := arguments[index]
if argument == "--" {
return fmt.Errorf("the -- argument terminator is not allowed")
}
if strings.HasPrefix(argument, "--") {
nameAndValue := strings.TrimPrefix(argument, "--")
name, _, hasValue := strings.Cut(nameAndValue, "=")
if name == "" {
return fmt.Errorf("invalid empty long flag")
}
if name == "help" {
return fmt.Errorf("must demonstrate execution, not only --help")
}
flag := runtimeCommandFlag(command, name)
if flag == nil {
return fmt.Errorf("uses unknown flag --%s", name)
}
providedFacts[flag.Name] = true
if !hasValue && flag.NoOptDefVal == "" {
if index+1 >= len(arguments) {
return fmt.Errorf("flag --%s requires a value", name)
}
index++
}
continue
}
if !strings.HasPrefix(argument, "-") || argument == "-" {
positionals = append(positionals, argument)
continue
}
shorthandsAndValue := strings.TrimPrefix(argument, "-")
shorthands, _, hasExplicitValue := strings.Cut(shorthandsAndValue, "=")
for offset := 0; offset < len(shorthands); offset++ {
shorthand := shorthands[offset : offset+1]
if shorthand[0] >= 0x80 {
return fmt.Errorf("uses invalid non-ASCII shorthand flag")
}
if shorthand == "h" {
return fmt.Errorf("must demonstrate execution, not only -h")
}
flag := runtimeCommandFlagByShorthand(command, shorthand)
if flag == nil {
return fmt.Errorf("uses unknown shorthand flag -%s", shorthand)
}
providedFacts[flag.Name] = true
if flag.NoOptDefVal == "" {
if offset+1 < len(shorthands) || hasExplicitValue {
break
}
if index+1 >= len(arguments) {
return fmt.Errorf("shorthand flag -%s requires a value", shorthand)
}
index++
break
}
}
}
missingRequired := make([]string, 0)
visitAgentExampleCommandFlags(command, func(flag *pflag.Flag) {
if flag != nil && len(flag.Annotations[cobra.BashCompOneRequiredFlag]) > 0 && !providedFacts[flag.Name] {
missingRequired = append(missingRequired, "--"+flag.Name)
}
})
if len(missingRequired) != 0 {
sort.Strings(missingRequired)
return fmt.Errorf("missing required flag(s): %s", strings.Join(missingRequired, ", "))
}
missingPositionals := make([]string, 0)
for _, positional := range positionalSpecs {
provided := positional.Index >= 0 && positional.Index < len(positionals)
if positional.Variadic {
provided = positional.Index >= 0 && len(positionals) > positional.Index
}
if provided && strings.TrimSpace(positional.Name) != "" {
providedFacts[strings.TrimLeft(strings.TrimSpace(positional.Name), "-")] = true
}
if positional.Required && !provided {
missingPositionals = append(missingPositionals, strings.TrimSpace(positional.Name))
}
}
if len(missingPositionals) != 0 {
sort.Strings(missingPositionals)
return fmt.Errorf("missing required positional argument(s): %s", strings.Join(missingPositionals, ", "))
}
if err := validateAgentExampleConstraints(providedFacts, constraints); err != nil {
return err
}
if command.Args != nil && len(positionalSpecs) == 0 {
if err := command.Args(command, positionals); err != nil {
return fmt.Errorf("invalid positional arguments: %w", err)
}
}
return nil
}
func mergeAgentExamplePositionals(groups ...[]contract.RuntimeSchemaPositional) []contract.RuntimeSchemaPositional {
byIdentity := map[string]contract.RuntimeSchemaPositional{}
for _, group := range groups {
for _, positional := range group {
key := fmt.Sprintf("%d\x00%s", positional.Index, strings.TrimSpace(positional.Name))
if _, exists := byIdentity[key]; !exists {
byIdentity[key] = positional
}
}
}
result := make([]contract.RuntimeSchemaPositional, 0, len(byIdentity))
for _, positional := range byIdentity {
result = append(result, positional)
}
sort.Slice(result, func(i, j int) bool {
if result[i].Index != result[j].Index {
return result[i].Index < result[j].Index
}
return result[i].Name < result[j].Name
})
return result
}
func validateAgentExampleConstraints(provided map[string]bool, constraints RuntimeSchemaConstraints) error {
for _, group := range constraints.RequireOneOf {
if agentExampleProvidedFlagCount(provided, group) == 0 {
return fmt.Errorf("missing require_one_of flags: %s", agentExampleFlagGroup(group))
}
}
for _, group := range constraints.RequireTogether {
providedCount := agentExampleProvidedFlagCount(provided, group)
if providedCount != 0 && providedCount != len(group) {
return fmt.Errorf("incomplete require_together flags: %s", agentExampleFlagGroup(group))
}
}
for _, group := range constraints.MutuallyExclusive {
if agentExampleProvidedFlagCount(provided, group) > 1 {
return fmt.Errorf("mutually_exclusive flags used together: %s", agentExampleFlagGroup(group))
}
}
return nil
}
func agentExampleProvidedFlagCount(provided map[string]bool, group []string) int {
count := 0
seen := map[string]bool{}
for _, raw := range group {
name := strings.TrimLeft(strings.TrimSpace(raw), "-")
if name != "" && !seen[name] && provided[name] {
seen[name] = true
count++
}
}
return count
}
func agentExampleFlagGroup(group []string) string {
flags := make([]string, 0, len(group))
for _, raw := range group {
if name := strings.TrimLeft(strings.TrimSpace(raw), "-"); name != "" {
flags = append(flags, "--"+name)
}
}
sort.Strings(flags)
return strings.Join(flags, ", ")
}
func visitAgentExampleCommandFlags(command *cobra.Command, visit func(*pflag.Flag)) {
seen := map[string]bool{}
visitSet := func(flags *pflag.FlagSet) {
flags.VisitAll(func(flag *pflag.Flag) {
if seen[flag.Name] {
return
}
seen[flag.Name] = true
visit(flag)
})
}
visitSet(command.Flags())
for current := command; current != nil; current = current.Parent() {
visitSet(current.PersistentFlags())
}
}
func runtimeCommandFlagByShorthand(command *cobra.Command, shorthand string) *pflag.Flag {
if command == nil || len(shorthand) != 1 {
return nil
}
if flag := command.Flags().ShorthandLookup(shorthand); flag != nil {
return flag
}
for current := command; current != nil; current = current.Parent() {
if flag := current.PersistentFlags().ShorthandLookup(shorthand); flag != nil {
return flag
}
}
return nil
}