Files
Agent-System/internal/taskrun/backend.go
T
weichen 738bbe1579 feat(agent): add pi-native backend and drop cursor-cli
Keep the container pi-agent backend as the default while the in-process runtime is available beside it, and remove the unused cursor bridge.
2026-09-21 14:52:53 +00:00

92 lines
2.4 KiB
Go

package taskrun
import (
"errors"
"strings"
)
var (
ErrUnauthorizedBackend = errors.New("taskrun: unauthorized backend")
ErrSensitivePayload = errors.New("taskrun: sensitive input rejected")
)
// Allowed backends are resolved by server policy. Callers cannot inject arbitrary strings.
var allowedBackends = map[string]struct{}{
"stub": {},
"pi-agent": {},
"pi-native": {},
}
func isProductionEnv(env string) bool {
switch strings.ToLower(strings.TrimSpace(env)) {
case "main", "prod", "production":
return true
default:
return false
}
}
// ResolveBackend maps a caller hint onto the server allowlist.
// An empty requested value falls back to configured. Unauthorized values are rejected.
// Prefer ResolveBackendInEnv when the deployment environment is known.
func ResolveBackend(requested, configured string) (string, error) {
return ResolveBackendInEnv(requested, configured, "")
}
// ResolveBackendInEnv is ResolveBackend with environment policy:
// in main/prod, stub is never allowed (neither as request nor as empty fallback).
func ResolveBackendInEnv(requested, configured, env string) (string, error) {
requested = strings.TrimSpace(requested)
configured = strings.TrimSpace(configured)
prod := isProductionEnv(env)
if requested == "" {
if configured == "" {
if prod {
return "", ErrUnauthorizedBackend
}
return "stub", nil
}
if _, ok := allowedBackends[configured]; !ok {
return "", ErrUnauthorizedBackend
}
if prod && configured == "stub" {
return "", ErrUnauthorizedBackend
}
return configured, nil
}
if _, ok := allowedBackends[requested]; !ok {
return "", ErrUnauthorizedBackend
}
if prod && requested == "stub" {
return "", ErrUnauthorizedBackend
}
return requested, nil
}
func StripSecrets(meta map[string]any) map[string]any {
if meta == nil {
return map[string]any{}
}
out := make(map[string]any, len(meta))
for k, v := range meta {
lk := strings.ToLower(strings.TrimSpace(k))
if lk == "authorization" || lk == "api_key" || lk == "token" || lk == "password" ||
strings.Contains(lk, "secret") || strings.Contains(lk, "credential") {
continue
}
out[k] = v
}
return out
}
func rejectSensitiveText(parts ...string) error {
for _, p := range parts {
low := strings.ToLower(p)
if strings.Contains(low, "api_key=") || strings.Contains(low, "authorization: bearer") {
return ErrSensitivePayload
}
}
return nil
}