Ctrl K
ComunidadGuías…

internal/util/claude_tool_id.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package util

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"sync/atomic"
	"time"
)

const geminiClaudeToolUseIDPrefix = "cpa_gemini_"

var (
	claudeToolUseIDSanitizer    = regexp.MustCompile(`[^a-zA-Z0-9_-]`)
	claudeFunctionNameSanitizer = regexp.MustCompile(`[^a-zA-Z0-9_-]`)
	claudeToolUseIDCounter      uint64
)

// SanitizeClaudeToolID ensures the given id conforms to Claude's
// tool_use.id regex ^[a-zA-Z0-9_-]+$.  Non-conforming characters are
// replaced with '_'; an empty result gets a generated fallback.
func SanitizeClaudeToolID(id string) string {
	s := claudeToolUseIDSanitizer.ReplaceAllString(id, "_")
	if s == "" {
		s = fmt.Sprintf("toolu_%d_%d", time.Now().UnixNano(), atomic.AddUint64(&claudeToolUseIDCounter, 1))
	}
	return s
}

// SanitizeClaudeFunctionName ensures a function or tool name conforms to Claude's
// requirements: ^[a-zA-Z0-9_-]{1,64}$. Non-conforming characters (including dots and colons
// commonly emitted by MCP tools) are replaced with '_'.
func SanitizeClaudeFunctionName(name string) string {
	if name == "" {
		return ""
	}
	s := claudeFunctionNameSanitizer.ReplaceAllString(name, "_")
	if len(s) > 64 {
		s = s[:64]
	}
	if s == "" {
		s = "_"
	}
	return s
}

// GeminiClaudeToolUseID returns a stable Claude-facing ID for a provider-native
// Gemini function call. The opaque ID lets the executor recover the exact
// provider call from its replay ledger instead of trusting client-mutated args.
func GeminiClaudeToolUseID(callID, name, argsRaw string) string {
	callID = strings.TrimSpace(callID)
	name = strings.TrimSpace(name)
	if callID == "" || name == "" {
		return ""
	}
	if strings.TrimSpace(argsRaw) != "" {
		var value any
		if json.Unmarshal([]byte(argsRaw), &value) == nil {
			if canonical, errMarshal := json.Marshal(value); errMarshal == nil {
				argsRaw = string(canonical)
			}
		} else {
			argsRaw = strings.TrimSpace(argsRaw)
		}
	}
	sum := sha256.Sum256([]byte(strings.Join([]string{callID, name, argsRaw}, "\x00")))
	return geminiClaudeToolUseIDPrefix + hex.EncodeToString(sum[:16])
}

// IsGeminiClaudeToolUseID reports whether id belongs to the reserved
// Claude-facing Gemini provenance namespace.
func IsGeminiClaudeToolUseID(id string) bool {
	id = strings.TrimSpace(id)
	if !strings.HasPrefix(id, geminiClaudeToolUseIDPrefix) {
		return false
	}
	digest := strings.TrimPrefix(id, geminiClaudeToolUseIDPrefix)
	if len(digest) != 32 {
		return false
	}
	_, errDecode := hex.DecodeString(digest)
	return errDecode == nil
}