diff --git a/agent/assistant/agent.go b/agent/assistant/agent.go index 07c29e0c..d834dfdc 100644 --- a/agent/assistant/agent.go +++ b/agent/assistant/agent.go @@ -183,6 +183,14 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa } sandboxCleanup = v2Cleanup ctx.Logger.PhaseComplete("Sandbox V2") + if v2Computer != nil { + ci := v2Computer.ComputerInfo() + ctx.Logger.Trace("Node: %s (%s)", ci.NodeID, ci.Kind) + if ci.BoxID != "" { + ctx.Logger.Trace("Computer: %s", ci.BoxID) + } + ctx.Logger.Trace("Workspace: %s", ast.SandboxV2.WorkspaceID) + } } else if ast.HasSandbox() { ctx.Logger.Phase("Sandbox") var err error diff --git a/agent/robot/events/integrations/telegram/polling.go b/agent/robot/events/integrations/telegram/polling.go index f14eeb80..75855921 100644 --- a/agent/robot/events/integrations/telegram/polling.go +++ b/agent/robot/events/integrations/telegram/polling.go @@ -4,6 +4,7 @@ import ( "context" "time" + kunlog "github.com/yaoapp/kun/log" tgapi "github.com/yaoapp/yao/integrations/telegram" ) @@ -34,7 +35,7 @@ func (a *Adapter) pollLoop() { func (a *Adapter) pollAll() { entries := a.snapshot() - log.Debug("pollAll bots=%d", len(entries)) + kunlog.Trace("[robot:telegram] pollAll bots=%d", len(entries)) if len(entries) == 0 { return } @@ -49,7 +50,7 @@ func (a *Adapter) pollAll() { default: } - log.Debug("polling robot=%s offset=%d", entry.robotID, entry.offset) + kunlog.Trace("[robot:telegram] polling robot=%s offset=%d", entry.robotID, entry.offset) groups := []string{"telegram", entry.robotID} msgs, err := entry.bot.GetUpdates(ctx, entry.offset, pollTimeout, groups) if err != nil { diff --git a/agent/sandbox/v2/claude/parse.go b/agent/sandbox/v2/claude/parse.go index 86cfb227..473167ac 100644 --- a/agent/sandbox/v2/claude/parse.go +++ b/agent/sandbox/v2/claude/parse.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/agent/output/message" ) @@ -76,9 +77,9 @@ func (p *streamParser) parse(ctx context.Context, stdout io.ReadCloser) error { var msg map[string]any if err := json.Unmarshal([]byte(line), &msg); err != nil { if len(line) > 200 { - fmt.Printf("[claude-parse] JSON unmarshal error: %v (line len=%d, prefix=%q)\n", err, len(line), line[:200]) + log.Trace("[claude-parse] JSON unmarshal error: %v (line len=%d, prefix=%q)", err, len(line), line[:200]) } else { - fmt.Printf("[claude-parse] JSON unmarshal error: %v (line=%q)\n", err, line) + log.Trace("[claude-parse] JSON unmarshal error: %v (line=%q)", err, line) } continue } @@ -107,7 +108,7 @@ func (p *streamParser) parse(ctx context.Context, stdout io.ReadCloser) error { } if err := scanner.Err(); err != nil { - fmt.Printf("[claude-parse] scanner error: %v (ctx.Err=%v)\n", err, ctx.Err()) + log.Trace("[claude-parse] scanner error: %v (ctx.Err=%v)", err, ctx.Err()) if ctx.Err() != nil { return ctx.Err() } diff --git a/agent/sandbox/v2/lifecycle.go b/agent/sandbox/v2/lifecycle.go index 24fe92b3..559403c2 100644 --- a/agent/sandbox/v2/lifecycle.go +++ b/agent/sandbox/v2/lifecycle.go @@ -5,11 +5,11 @@ import ( "crypto/rand" "encoding/hex" "fmt" - "log" mathrand "math/rand" "strings" "github.com/yaoapp/gou/connector" + "github.com/yaoapp/kun/log" agentContext "github.com/yaoapp/yao/agent/context" "github.com/yaoapp/yao/agent/sandbox/v2/types" infra "github.com/yaoapp/yao/sandbox/v2" @@ -29,11 +29,7 @@ func BuildIdentifier(cfg *types.SandboxConfig, ownerID, chatID, assistantID, wor case "session": return fmt.Sprintf("%s-%s-%s", ownerID, assistantID, chatID) case "longrunning", "persistent": - wsKey := workspaceID - if wsKey == "" { - wsKey = ownerID - } - return fmt.Sprintf("%s-%s.%s", ownerID, assistantID, wsKey) + return fmt.Sprintf("%s-%s.%s", ownerID, assistantID, workspaceID) default: return "" } @@ -58,12 +54,12 @@ func ResolveNodeID(ctx *agentContext.Context, cfg *types.SandboxConfig, manager } ownerID := resolveOwnerID(ctx) - fmt.Printf("[sandbox/v2] ResolveNodeID: computerID=%q workspaceID=%q ownerID=%q image=%q\n", computerID, workspaceID, ownerID, cfg.Computer.Image) + log.Trace("[sandbox/v2] ResolveNodeID: computerID=%q workspaceID=%q ownerID=%q image=%q", computerID, workspaceID, ownerID, cfg.Computer.Image) if workspaceID != "" { wsNode, err := workspace.M().NodeForWorkspace(context.Background(), workspaceID) if err == nil && wsNode != "" { - fmt.Printf("[sandbox/v2] ResolveNodeID: workspace %s -> node %s\n", workspaceID, wsNode) + log.Trace("[sandbox/v2] ResolveNodeID: workspace %s -> node %s", workspaceID, wsNode) computerID = wsNode } } @@ -73,29 +69,29 @@ func ResolveNodeID(ctx *agentContext.Context, cfg *types.SandboxConfig, manager if err != nil { return "", "", fmt.Errorf("auto-select node for ResolveNodeID: %w", err) } - fmt.Printf("[sandbox/v2] ResolveNodeID: pickNodeByFilter -> %s\n", pickedID) + log.Trace("[sandbox/v2] ResolveNodeID: pickNodeByFilter -> %s", pickedID) computerID = pickedID cfg.NodeID = pickedID } if node, ok := tai.GetNodeMeta(computerID); ok { hasContainerRuntime := node.Capabilities.Docker || node.Capabilities.K8s - fmt.Printf("[sandbox/v2] ResolveNodeID: node=%q HostExec=%v Docker=%v K8s=%v hasContainer=%v\n", computerID, node.Capabilities.HostExec, node.Capabilities.Docker, node.Capabilities.K8s, hasContainerRuntime) + log.Trace("[sandbox/v2] ResolveNodeID: node=%q HostExec=%v Docker=%v K8s=%v hasContainer=%v", computerID, node.Capabilities.HostExec, node.Capabilities.Docker, node.Capabilities.K8s, hasContainerRuntime) if node.Capabilities.HostExec && !hasContainerRuntime { - fmt.Println("[sandbox/v2] ResolveNodeID: -> host (host-only node)") + log.Trace("[sandbox/v2] ResolveNodeID: -> host (host-only node)") return computerID, "host", nil } if node.Capabilities.HostExec && hasContainerRuntime && cfg.Computer.Image == "" { - fmt.Println("[sandbox/v2] ResolveNodeID: -> host (dual-capable, no image)") + log.Trace("[sandbox/v2] ResolveNodeID: -> host (dual-capable, no image)") return computerID, "host", nil } if !hasContainerRuntime { return "", "", fmt.Errorf("node %q has no container runtime and no host_exec capability", computerID) } - fmt.Println("[sandbox/v2] ResolveNodeID: -> box") + log.Trace("[sandbox/v2] ResolveNodeID: -> box") return computerID, "box", nil } - fmt.Printf("[sandbox/v2] ResolveNodeID: node %q not found in registry, assuming box\n", computerID) + log.Trace("[sandbox/v2] ResolveNodeID: node %q not found in registry, assuming box", computerID) return computerID, "box", nil } @@ -133,20 +129,20 @@ func GetComputer(ctx *agentContext.Context, cfg *types.SandboxConfig, manager *i wsNode, err := workspace.M().NodeForWorkspace(context.Background(), workspaceID) if err == nil && wsNode != "" { if computerID != "" && computerID != wsNode { - log.Printf("[sandbox/v2] workspace %s bound to node %s overrides computer_id %s", workspaceID, wsNode, computerID) + log.Trace("[sandbox/v2] workspace %s bound to node %s overrides computer_id %s", workspaceID, wsNode, computerID) } computerID = wsNode } } - fmt.Printf("[sandbox/v2] GetComputer: computerID=%q workspaceID=%q ownerID=%q cfgNodeID=%q image=%q\n", computerID, workspaceID, ownerID, cfg.NodeID, cfg.Computer.Image) + log.Trace("[sandbox/v2] GetComputer: computerID=%q workspaceID=%q ownerID=%q cfgNodeID=%q image=%q", computerID, workspaceID, ownerID, cfg.NodeID, cfg.Computer.Image) if computerID != "" { - fmt.Printf("[sandbox/v2] GetComputer: -> resolveComputerByID(%s)\n", computerID) + log.Trace("[sandbox/v2] GetComputer: -> resolveComputerByID(%s)", computerID) return resolveComputerByID(cfg, manager, computerID, ownerID, identifier, workspaceID, conn...) } - fmt.Println("[sandbox/v2] GetComputer: -> resolveComputerByDSL (no computerID)") + log.Trace("[sandbox/v2] GetComputer: -> resolveComputerByDSL (no computerID)") return resolveComputerByDSL(cfg, manager, ownerID, identifier, workspaceID, conn...) } @@ -162,10 +158,10 @@ func resolveComputerByID( if node, ok := tai.GetNodeMeta(computerID); ok { cfg.NodeID = computerID hasContainerRuntime := node.Capabilities.Docker || node.Capabilities.K8s - fmt.Printf("[sandbox/v2] resolveComputerByID: node=%q found=true HostExec=%v Docker=%v K8s=%v hasContainer=%v image=%q\n", computerID, node.Capabilities.HostExec, node.Capabilities.Docker, node.Capabilities.K8s, hasContainerRuntime, cfg.Computer.Image) + log.Trace("[sandbox/v2] resolveComputerByID: node=%q found=true HostExec=%v Docker=%v K8s=%v hasContainer=%v image=%q", computerID, node.Capabilities.HostExec, node.Capabilities.Docker, node.Capabilities.K8s, hasContainerRuntime, cfg.Computer.Image) if node.Capabilities.HostExec && !hasContainerRuntime { - fmt.Println("[sandbox/v2] resolveComputerByID: -> host (host-only node)") + log.Trace("[sandbox/v2] resolveComputerByID: -> host (host-only node)") cfg.Kind = "host" host, err := manager.Host(context.Background(), computerID) if err != nil { @@ -215,18 +211,18 @@ func resolveComputerByDSL( conn ...connector.Connector, ) (infra.Computer, string, error) { - fmt.Printf("[sandbox/v2] resolveComputerByDSL: cfgNodeID=%q image=%q\n", cfg.NodeID, cfg.Computer.Image) + log.Trace("[sandbox/v2] resolveComputerByDSL: cfgNodeID=%q image=%q", cfg.NodeID, cfg.Computer.Image) if cfg.NodeID == "" { pickedID, err := pickNodeByFilter(cfg.Filter, cfg.Computer.Image) if err != nil { return nil, identifier, fmt.Errorf("auto-select node: %w", err) } - fmt.Printf("[sandbox/v2] resolveComputerByDSL: pickNodeByFilter -> %s\n", pickedID) + log.Trace("[sandbox/v2] resolveComputerByDSL: pickNodeByFilter -> %s", pickedID) cfg.NodeID = pickedID } - fmt.Printf("[sandbox/v2] resolveComputerByDSL: -> resolveComputerByID(%s)\n", cfg.NodeID) + log.Trace("[sandbox/v2] resolveComputerByDSL: -> resolveComputerByID(%s)", cfg.NodeID) return resolveComputerByID(cfg, manager, cfg.NodeID, ownerID, identifier, workspaceID, conn...) } @@ -240,6 +236,10 @@ func resolveBox( if workspaceID == "" && cfg.NodeID != "" { workspaceID = workspace.DefaultWorkspaceID(ownerID, cfg.NodeID) cfg.WorkspaceID = workspaceID + if dot := strings.LastIndex(identifier, "."); dot >= 0 { + identifier = identifier[:dot+1] + workspaceID + cfg.ID = identifier + } } // Reuse: non-empty identifier → try Get first. @@ -248,7 +248,7 @@ func resolveBox( if err == nil && box != nil { if box.IsStopped() { if startErr := manager.StartBox(context.Background(), identifier); startErr != nil { - log.Printf("[sandbox/v2] auto-start stopped box %s failed: %v, creating new", identifier, startErr) + log.Trace("[sandbox/v2] auto-start stopped box %s failed: %v, creating new", identifier, startErr) } else { box.BindWorkplace(workspaceID) return box, identifier, nil @@ -269,7 +269,7 @@ func resolveBox( if err != nil { return nil, identifier, fmt.Errorf("build create options: %w", err) } - fmt.Printf("[sandbox/v2] resolveBox: createOpts NodeID=%q Image=%q WorkspaceID=%q ID=%q Owner=%q\n", createOpts.NodeID, createOpts.Image, createOpts.WorkspaceID, createOpts.ID, createOpts.Owner) + log.Trace("[sandbox/v2] resolveBox: createOpts NodeID=%q Image=%q WorkspaceID=%q ID=%q Owner=%q", createOpts.NodeID, createOpts.Image, createOpts.WorkspaceID, createOpts.ID, createOpts.Owner) // Oneshot with empty identifier: generate a random one. if createOpts.ID == "" { @@ -298,7 +298,7 @@ func LifecycleAction(ctx context.Context, cfg *types.SandboxConfig, computer inf case "oneshot": if info.Kind == "box" && manager != nil { if err := manager.Remove(ctx, cfg.ID); err != nil { - log.Printf("[sandbox/v2] oneshot remove %s: %v", cfg.ID, err) + log.Trace("[sandbox/v2] oneshot remove %s: %v", cfg.ID, err) } } diff --git a/openapi/workspace/workspace.go b/openapi/workspace/workspace.go index 1719e84b..fcdbb5f7 100644 --- a/openapi/workspace/workspace.go +++ b/openapi/workspace/workspace.go @@ -3,7 +3,6 @@ package workspace import ( "context" "encoding/base64" - "fmt" "io" "mime" "net/http" @@ -12,6 +11,7 @@ import ( "strings" "github.com/gin-gonic/gin" + "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/openapi/oauth/authorized" "github.com/yaoapp/yao/openapi/oauth/types" "github.com/yaoapp/yao/openapi/response" @@ -417,16 +417,16 @@ func handleReadFile(c *gin.Context) { path = path[1:] } - fmt.Printf("[workspace] handleReadFile id=%s path=%q\n", c.Param("id"), path) + log.Trace("[workspace] handleReadFile id=%s path=%q", c.Param("id"), path) data, err := mgr().ReadFile(context.Background(), c.Param("id"), path) if err != nil { - fmt.Printf("[workspace] ReadFile error: %v\n", err) + log.Trace("[workspace] ReadFile error: %v", err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - fmt.Printf("[workspace] ReadFile ok, size=%d, encoding=%q\n", len(data), c.Query("encoding")) + log.Trace("[workspace] ReadFile ok, size=%d, encoding=%q", len(data), c.Query("encoding")) if c.Query("encoding") == "base64" { response.RespondWithSuccess(c, http.StatusOK, gin.H{ @@ -441,7 +441,7 @@ func handleReadFile(c *gin.Context) { if mimeType == "" { mimeType = "application/octet-stream" } - fmt.Printf("[workspace] serving ext=%q mime=%q size=%d\n", ext, mimeType, len(data)) + log.Trace("[workspace] serving ext=%q mime=%q size=%d", ext, mimeType, len(data)) c.Data(http.StatusOK, mimeType, data) }