1package agent
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "sync/atomic"
8
9 "github.com/patriceckhart/zot/packages/agent/extensions"
10 "github.com/patriceckhart/zot/packages/agent/extproto"
11 "github.com/patriceckhart/zot/packages/core"
12)
13
14var extensionCallSeq atomic.Uint64
15
16type extensionOriginKey struct{}
17
18// extensionToolCaller keeps host-initiated calls out of model transcripts,
19// while sharing the active agent's guard and confirmation path.
20func extensionToolCaller(mgr *extensions.Manager, active func() *core.Agent) func(context.Context, string, string, json.RawMessage) extproto.ToolResultFromHost {
21 return func(ctx context.Context, origin, name string, args json.RawMessage) extproto.ToolResultFromHost {
22 out := extproto.ToolResultFromHost{Name: name}
23 ag := active()
24 if ag == nil {
25 out.IsError = true
26 out.Content = []extproto.ContentBlock{{Type: "text", Text: "no active agent"}}
27 return out
28 }
29 id := fmt.Sprintf("ext-call-%d", extensionCallSeq.Add(1))
30 defer mgr.TrackToolCall(id, origin)()
31 sink := func(ev core.AgentEvent) {
32 switch e := ev.(type) {
33 case core.EvToolCall:
34 mgr.EmitEvent(extproto.EventFromHost{Event: "tool_call", OriginExtension: origin, ToolID: e.ID, ToolName: e.Name, ToolArgs: e.Args})
35 case core.EvToolResult:
36 mgr.EmitEvent(extproto.EventFromHost{Event: "tool_result", OriginExtension: origin, ToolID: e.ID, ToolName: e.Name, ToolArgs: e.Args, Status: e.Status, Executed: &e.Executed, Result: extensionEventResult(e.Result)})
37 }
38 }
39 res := ag.CallTool(context.WithValue(ctx, extensionOriginKey{}, origin), id, name, args, sink)
40 visible := extensionEventResult(res)
41 out.Content, out.IsError, out.Truncated = visible.Content, visible.IsError, visible.Truncated
42 return out
43 }
44}