161 lines
5.9 KiB
Go
161 lines
5.9 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 chat
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
|
|
)
|
|
|
|
const reviewedChatShortcutInterfaceReason = "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref."
|
|
|
|
// reviewedChatShortcutContractCommands is the exact reviewed migration ledger
|
|
// for Chat shortcuts that were delivered by the former committed Schema
|
|
// catalog but did not yet carry #830's typed Contract declaration. Exact names
|
|
// deliberately prevent a future shortcut from entering Schema by accident.
|
|
var reviewedChatShortcutContractCommands = map[string]struct{}{
|
|
"+category-add-conversation": {},
|
|
"+category-list-conversations": {},
|
|
"+category-remove-conversation": {},
|
|
"+chat-add-bot": {},
|
|
"+chat-audit-join": {},
|
|
"+chat-create": {},
|
|
"+chat-get-by-id": {},
|
|
"+chat-list": {},
|
|
"+chat-members-get": {},
|
|
"+chat-mute-member": {},
|
|
"+chat-quit": {},
|
|
"+chat-remove-bot": {},
|
|
"+chat-role-remove": {},
|
|
"+chat-role-remove-user": {},
|
|
"+chat-transfer-owner": {},
|
|
"+chat-update": {},
|
|
"+chat-update-icon": {},
|
|
"+chat-update-settings": {},
|
|
"+conversation-clear-messages": {},
|
|
"+conversation-clear-red-point": {},
|
|
"+conversation-hide": {},
|
|
"+conversation-mark-read": {},
|
|
"+conversation-mark-unread": {},
|
|
"+conversation-mute": {},
|
|
"+conversation-set-top": {},
|
|
"+feed-group-query-item": {},
|
|
"+flag-cancel": {},
|
|
"+flag-create": {},
|
|
"+flag-list": {},
|
|
"+messages-add-emoji": {},
|
|
"+messages-add-text-emotion": {},
|
|
"+messages-batch-recall-by-bot": {},
|
|
"+messages-batch-send-by-bot": {},
|
|
"+messages-combine-forward": {},
|
|
"+messages-create-text-emotion": {},
|
|
"+messages-forward": {},
|
|
"+messages-forward-topic": {},
|
|
"+messages-list": {},
|
|
"+messages-recall": {},
|
|
"+messages-recall-by-bot": {},
|
|
"+messages-remove-emoji": {},
|
|
"+messages-remove-text-emotion": {},
|
|
"+messages-reply": {},
|
|
"+messages-resource-download": {},
|
|
"+messages-resource-url": {},
|
|
"+messages-send-by-bot": {},
|
|
"+messages-set-pin": {},
|
|
"+messages-set-top": {},
|
|
"+messages-unset-pin": {},
|
|
"+messages-unset-top": {},
|
|
}
|
|
|
|
// withReviewedChatShortcutContracts ports the previously reviewed Chat Schema
|
|
// records into #830's typed declaration model. Existing explicit Contracts are
|
|
// preserved. Missing Contracts must be listed in the exact ledger above.
|
|
func withReviewedChatShortcutContracts(values ...shortcut.Shortcut) []shortcut.Shortcut {
|
|
out := make([]shortcut.Shortcut, len(values))
|
|
for i, value := range values {
|
|
out[i] = value
|
|
if !value.Contract.Empty() {
|
|
continue
|
|
}
|
|
if _, reviewed := reviewedChatShortcutContractCommands[value.Command]; !reviewed {
|
|
continue
|
|
}
|
|
out[i].Safety = reviewedChatShortcutSafety(value.Risk)
|
|
out[i].Contract = reviewedChatShortcutContract(value)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func reviewedChatShortcutSafety(risk shortcut.Risk) contract.SafetySpec {
|
|
switch risk {
|
|
case shortcut.RiskWrite:
|
|
return contract.SafetySpec{
|
|
Effect: "write", Risk: "medium",
|
|
Confirmation: "user_required", Idempotency: "unknown",
|
|
}
|
|
case shortcut.RiskHighWrite:
|
|
return contract.SafetySpec{
|
|
Effect: "destructive", Risk: "high",
|
|
Confirmation: "user_required", Idempotency: "unknown",
|
|
}
|
|
default:
|
|
return contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
}
|
|
}
|
|
}
|
|
|
|
func reviewedChatShortcutContract(value shortcut.Shortcut) corecmd.ContractDecl {
|
|
name := "shortcut_" + strings.ReplaceAll(strings.TrimPrefix(value.Command, "+"), "-", "_")
|
|
cliPath := value.Service + " " + value.Command
|
|
aliases := make([]string, 0, len(value.Aliases))
|
|
for _, alias := range value.Aliases {
|
|
aliases = append(aliases, value.Service+" "+alias)
|
|
}
|
|
description := strings.TrimSpace(value.Intent)
|
|
if description == "" {
|
|
description = strings.TrimSpace(value.Description)
|
|
}
|
|
examples := append([]string(nil), value.Tips...)
|
|
if len(examples) > 2 {
|
|
examples = examples[:2]
|
|
}
|
|
return corecmd.ContractDecl{
|
|
Title: value.Description,
|
|
Description: description,
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: contract.InterfaceModeComposite,
|
|
Availability: contract.InterfaceAvailable,
|
|
Reason: reviewedChatShortcutInterfaceReason,
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: value.Description,
|
|
UseWhen: []string{description},
|
|
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
|
Examples: examples,
|
|
},
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: value.Service,
|
|
Name: name,
|
|
CanonicalPath: value.Service + "." + name,
|
|
CLIPath: cliPath,
|
|
PrimaryCLIPath: cliPath,
|
|
Aliases: aliases,
|
|
},
|
|
}
|
|
}
|