// subagent's log. The toolbox of the agent has the runs of its subagents for
// spawn and tell, the WhatsApp link for the WhatsApp tools, and the schedules
// for the schedule tools. Each permanent agent has the parent-child queue. Only
-// the root has child creation. A child has no WhatsApp link or child creation.
-// The toolbox of a subagent has none of them, so these are unknown tools there.
+// the root has child creation and source routes. A child has neither of them
+// nor a WhatsApp link. The toolbox of a subagent has none of them, so these are
+// unknown tools there.
type Toolbox struct {
log ToolLog
chat ToolChat
agentName string
messages *DaemonMessages
children *DaemonChildren
+ routes *Routes
+ images *ImageStore
}
// Call logs the call of the tool name with its JSON arguments, makes the log
-// durable, runs the tool, and logs its clipped output, which it returns. A
-// failed tool gives its error as the output. An error of Call itself is an
-// error of the log. When ctx ends before the flush, the tool does not run.
-func (b *Toolbox) Call(ctx context.Context, name, arguments string) (string, error) {
+// durable, runs the tool, and logs its clipped text. It returns that text or
+// the text and image parts of a single-message zoom. Pixels never enter the
+// log. A failed tool gives its error as the output. An error of Call itself is
+// an error of the log. When ctx ends before the flush, the tool does not run.
+func (b *Toolbox) Call(ctx context.Context, name, arguments string) (any, error) {
_, err := b.log.Append("tool", toolCallText(name, arguments))
if err != nil {
return "", fmt.Errorf("append tool call: %w", err)
output = toolClip(b.run(ctx, name, arguments))
}
+ var (
+ result any = output
+ resultErr error
+ )
+
+ if err == nil {
+ result, resultErr = imageToolOutput(b, name, arguments, output)
+ }
+
+ if resultErr != nil {
+ output = "error: " + resultErr.Error()
+ result = output
+ }
+
_, err = b.log.Append("echo", output)
if err != nil {
return "", fmt.Errorf("append tool result: %w", err)
}
- return output, nil
+ return result, nil
}
// run runs one tool and returns its output, or its error as text.
case err == nil:
output, err = b.children.Create(args.Name, args.Responsibility)
}
+ case "route", "unroute", "routes":
+ var args ToolRouteArguments
+
+ switch name {
+ case "routes":
+ err = toolDecodeArguments(arguments, &struct{}{})
+ case "unroute":
+ var source ToolRouteSourceArguments
+
+ err = toolDecodeArguments(arguments, &source)
+ args.Source = source.Source
+ default:
+ err = toolDecodeArguments(arguments, &args)
+ }
+
+ switch {
+ case b.routes == nil:
+ err = fmt.Errorf("unknown tool %q", name)
+ case err != nil:
+ case name == "routes":
+ output = b.routes.List()
+ case name == "route" && (b.messages == nil || b.messages.Child(b.agentName, args.Agent) == nil):
+ err = errors.New("the agent must be a child of the root")
+ default:
+ err = b.routes.Set(args.Source, args.Agent)
+ output = "Route removed."
+ if name == "route" {
+ output = "Route set."
+ }
+ }
case "tell":
var args ToolTellArguments
Responsibility string `json:"responsibility"`
}
+// ToolRouteArguments are the arguments of route.
+type ToolRouteArguments struct {
+ Source string `json:"source"`
+ Agent string `json:"agent"`
+}
+
+// ToolRouteSourceArguments are the arguments of unroute.
+type ToolRouteSourceArguments struct {
+ Source string `json:"source"`
+}
+
// ToolTellArguments are the arguments of tell.
type ToolTellArguments struct {
Name string `json:"name"`