1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
package main
import (
"context"
"fmt"
"io"
"mm/internal/config"
"mm/internal/decide"
"mm/internal/engine"
)
// runDecisionPrompt is -p for a decider agent: the server's JSON alone on
// out, so that a hook or a script can parse it, and an exit status a pipe
// can test (0 answered, 1 failed). Like runStructuredPrompt, everything
// human goes to stderr.
//
// mm -config agent.decider.yaml -p "rm -rf ~/data" | jq '.answers.destructive.noul'
func runDecisionPrompt(ctx context.Context, e *engine.Engine, prompt string, out io.Writer) int {
res, err := e.Decide(ctx, prompt)
if err != nil {
fail("[decision: %v]", err)
return 1
}
_, _ = fmt.Fprintln(out, string(res.Raw))
return 0
}
// installRouter installs the process's skill and tool routers when the
// config has deciders for them. A decider file that cannot be used is a
// start-up error: the operator asked for routing.
func installRouter() error {
routing = ""
if settings := config.Cfg.SkillRouting; settings.Enabled() {
r, err := engine.NewRouter(settings)
if err != nil {
return err
}
decide.SetRouter(r)
routing = fmt.Sprintf(" | routing: %s/%s ≥ %.2f", r.Client.Provider, r.Client.Model, r.MinConfidence)
}
if settings := config.Cfg.ToolRouting; settings.Enabled() {
r, err := engine.NewToolRouter(settings)
if err != nil {
return err
}
decide.SetToolRouter(r)
routing += fmt.Sprintf(" | tool routing: %s/%s, %d group(s), drop < %.2f", r.Client.Provider, r.Client.Model, len(r.Groups), r.DropBelow)
}
return nil
}
// routing is the banner's column for skill and tool routing, "" when off.
var routing string
// decisionToolColumn is the banner's tools column for a decider agent.
func decisionToolColumn() string {
n := len(config.Cfg.Decision.Questions)
return fmt.Sprintf("none (decision model: %d question(s))", n)
}
|