feat(canvas): 云端 Agent - 完成策略分层、节点能力注册与可解释审批改造

This commit is contained in:
ddCat
2026-09-14 17:58:21 +08:00
parent 4b0e9d8965
commit 01486872c4
65 changed files with 5347 additions and 918 deletions

View File

@@ -1,63 +1,49 @@
package app
// cloudAgentNodeCapability is the server-side contract for nodes that an Agent
// may create or connect. It is intentionally independent from the browser node
// registry: renderers and plugin schemas are not an authorization boundary.
type cloudAgentNodeCapability struct {
Type string
Label string
DefaultWidth float64
DefaultHeight float64
InputKind string
GenerationMode string
AcceptedInputKinds []string
MaxInputCount int
CanUpdate bool
import "infinite-canvas/backend/internal/canvas/capability"
// Canvas capabilities are registered once at the canvas domain boundary. Agent
// tools, creation, media and state projection all consume this registry; there
// is no Agent-only node allow-list to keep in sync.
type cloudAgentNodeCapability = capability.Descriptor
type CloudAgentCapabilitySet struct {
Version string
Hash string
Nodes []string
}
var cloudAgentNodeCapabilities = []cloudAgentNodeCapability{
{Type: "text", Label: "文本", DefaultWidth: 340, DefaultHeight: 240, InputKind: "text", CanUpdate: true},
{Type: "markdown", Label: "Markdown", DefaultWidth: 420, DefaultHeight: 320, InputKind: "text", CanUpdate: true},
{Type: "image", Label: "图片", DefaultWidth: 720, DefaultHeight: 405, InputKind: "image", GenerationMode: "image", AcceptedInputKinds: []string{"text", "image"}},
{Type: "video", Label: "视频", DefaultWidth: 720, DefaultHeight: 405, InputKind: "video", GenerationMode: "video", AcceptedInputKinds: []string{"text", "image", "video", "audio"}},
// The current audio provider has no character-image/voice binding input. Do
// not advertise image references until the provider path consumes them.
{Type: "audio", Label: "音频", DefaultWidth: 340, DefaultHeight: 120, InputKind: "audio", GenerationMode: "audio", AcceptedInputKinds: []string{"text"}, MaxInputCount: 1},
{Type: "frame", Label: "背板", DefaultWidth: 760, DefaultHeight: 520},
{Type: "script", Label: "分镜脚本", DefaultWidth: 920, DefaultHeight: 360},
}
const cloudAgentCapabilitySetVersion = capability.SetVersion
var canvasCapabilityRegistry = capability.BuiltinRegistry()
func cloudAgentNodeCapabilityForType(nodeType string) (cloudAgentNodeCapability, bool) {
for _, capability := range cloudAgentNodeCapabilities {
if capability.Type == nodeType {
return capability, true
}
}
return cloudAgentNodeCapability{}, false
return canvasCapabilityRegistry.Resolve(nodeType)
}
func cloudAgentNodeTypeNames() []string {
names := make([]string, 0, len(cloudAgentNodeCapabilities))
for _, capability := range cloudAgentNodeCapabilities {
names = append(names, capability.Type)
}
return names
}
func cloudAgentNodeTypeNames() []string { return canvasCapabilityRegistry.Types() }
// cloudAgentGenerationModes is the single source of truth for generation
// capabilities exposed to Agent. Keep this derived from the node contract so a
// newly supported generation node cannot be accidentally omitted from model
// discovery while still being accepted by the write path.
func cloudAgentGenerationModes() map[string]bool {
modes := make(map[string]bool)
for _, capability := range cloudAgentNodeCapabilities {
if capability.GenerationMode != "" {
modes[capability.GenerationMode] = true
func cloudAgentGenerationModeNames() []string {
modes := make([]string, 0)
for _, mode := range canvasCapabilityRegistry.GenerationModeNames() {
if cloudAgentGenerationModeSupported(mode) {
modes = append(modes, mode)
}
}
return modes
}
func cloudAgentGenerationModeSupported(mode string) bool {
return cloudAgentGenerationModes()[mode]
_, implemented := cloudAgentGenerationAdapters[mode]
return implemented && canvasCapabilityRegistry.SupportsGenerationMode(mode)
}
func cloudAgentNodeCapabilityForGenerationMode(mode string) (cloudAgentNodeCapability, bool) {
return canvasCapabilityRegistry.ResolveGenerationMode(mode)
}
func cloudAgentCapabilitySetHash() string { return canvasCapabilityRegistry.Hash() }
func CloudAgentCapabilitySetInfo() CloudAgentCapabilitySet {
return CloudAgentCapabilitySet{Version: cloudAgentCapabilitySetVersion, Hash: cloudAgentCapabilitySetHash(), Nodes: cloudAgentNodeTypeNames()}
}