返回 DeepSeek-Reasonix
usecapability_batch.go
根目录 / internal / agent / usecapability_batch.go
1 package agent
2
3 import (
4 "encoding/json"
5 "strings"
6
7 "reasonix/internal/tool"
8 )
9
10 // ClassifyCall resolves only local metadata. Execution still rechecks the live
11 // MCP generation and safety annotations under the runtime dispatch lock.
12 func (t *UseCapabilityTool) ClassifyCall(raw json.RawMessage) tool.CallClass {
13 args, action, id, err := parseUseCapabilityArgs(raw)
14 if err != nil {
15 return tool.CallClass{}
16 }
17 switch action {
18 case "list", "search", "inspect":
19 return tool.CallClass{
20 Known: true, ReadOnly: true, ParallelSafe: true,
21 ResourceKey: "capability-catalog", Generation: "use-capability-v2",
22 }
23 case "call":
24 return t.classifyCapabilityTarget(id, args.Arguments)
25 default:
26 return tool.CallClass{}
27 }
28 }
29
30 func (t *UseCapabilityTool) classifyCapabilityTarget(id string, args json.RawMessage) tool.CallClass {
31 if server, raw, err := parseMCPCapabilityID(id); err == nil {
32 cached, source, ok := t.localMCPTool(server, raw)
33 if !ok || source != "shared_host" || !cached.ReadOnly || cached.Destructive || t.host == nil || !t.host.HasClient(server) {
34 return tool.CallClass{}
35 }
36 if t.runtime != nil && t.runtime.serverIsSerial(server) {
37 return tool.CallClass{Known: true, ReadOnly: true, ResourceKey: server + "/" + raw, Generation: tool.SchemaFingerprint(cached.Schema)}
38 }
39 return tool.CallClass{
40 Known: true, ReadOnly: true, ParallelSafe: true,
41 ResourceKey: server + "/" + raw, Generation: tool.SchemaFingerprint(cached.Schema),
42 }
43 }
44 if t.registry == nil || strings.HasPrefix(id, "skill:") || strings.HasPrefix(id, "task:") {
45 return tool.CallClass{}
46 }
47 name := ""
48 switch {
49 case strings.HasPrefix(id, "tool:"):
50 name = strings.TrimSpace(strings.TrimPrefix(id, "tool:"))
51 case strings.HasPrefix(id, "workflow:"):
52 name = strings.TrimSpace(strings.TrimPrefix(id, "workflow:"))
53 case strings.HasPrefix(id, "web:"), strings.HasPrefix(id, "lsp:"), strings.HasPrefix(id, "session:"), strings.HasPrefix(id, "memory:"):
54 if _, rest, ok := strings.Cut(id, ":"); ok {
55 name = strings.TrimSpace(rest)
56 }
57 }
58 if name == "" {
59 return tool.CallClass{}
60 }
61 target, ok := t.registry.Get(name)
62 if !ok || !target.ReadOnly() {
63 return tool.CallClass{}
64 }
65 // Evidence/order control tools remain serial even if their schema is
66 // nominally read-only.
67 switch target.Name() {
68 case "todo_write", "job_output", "wait", "bash_output", "compress":
69 return tool.CallClass{Known: true, ReadOnly: true}
70 }
71 _ = args // Reserved for target-specific classifiers.
72 return tool.CallClass{
73 Known: true, ReadOnly: true, ParallelSafe: true,
74 ResourceKey: target.Name(), Generation: tool.SchemaFingerprint(target.Schema()),
75 }
76 }
77
77 lines GO