Files
picoclaw/pkg/agent/turn_profile_policy.go
T
lxowalle 2992eccbf0 feat: add request-scoped context policies (#2914)
* feat: add request-scoped context policies

Add named turn profiles under agents.defaults so callers can opt into
per-request context and tool policies without changing default chat behavior.

Profiles can disable history, system context, skill prompts, or tools, and can
limit skills/tools with allow lists. Wire profile selection through Pico message
payloads, agent turn execution, Web chat selection, and Web visual config.

Reject invalid turn profiles before saving config through Web APIs and document
the new request context policy behavior.

* fix: address turn profile review blockers

* feat: simplify request context policy config

* fix: suppress tool prompt when turn tools are disabled

* fix: enforce turn profile tool restrictions
2026-05-22 10:06:40 +08:00

132 lines
3.2 KiB
Go

package agent
import (
"strings"
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/providers"
)
func resolveTurnProfileOptions(cfg *config.Config, opts processOptions) (processOptions, error) {
if cfg == nil {
return opts, nil
}
profile, ok, err := cfg.Agents.Defaults.ResolveTurnProfile()
if err != nil {
return opts, err
}
if !ok {
return opts, nil
}
opts.TurnProfile = profile
if profile.HistoryMode == config.TurnProfileModeOff {
opts.NoHistory = true
opts.EnableSummary = false
}
return opts, nil
}
func turnProfileSystemPromptOff(profile config.EffectiveTurnProfile) bool {
return profile.Enabled && profile.SystemPromptMode == config.TurnProfileModeOff
}
func turnProfileSkillsOff(profile config.EffectiveTurnProfile) bool {
return profile.Enabled && profile.SkillsMode == config.TurnProfileModeOff
}
func turnProfileCustomSkills(profile config.EffectiveTurnProfile) bool {
return profile.Enabled && profile.SkillsMode == config.TurnProfileModeCustom
}
func turnProfileHasCallableTools(
profile config.EffectiveTurnProfile,
defs []providers.ToolDefinition,
) bool {
if !profile.Enabled {
return true
}
return len(filterToolsByTurnProfile(defs, profile)) > 0
}
func turnProfileToolAllowed(profile config.EffectiveTurnProfile, name string) bool {
if !profile.Enabled {
return true
}
switch profile.ToolsMode {
case config.TurnProfileModeOff:
return false
case config.TurnProfileModeCustom:
allowed := cleanAllowedSet(profile.AllowedTools)
if len(allowed) == 0 {
return false
}
_, ok := allowed[strings.ToLower(strings.TrimSpace(name))]
return ok
default:
return true
}
}
func toolUseSystemPromptRule() string {
return "**ALWAYS use tools** - When you need to perform an action (schedule reminders, send messages, execute commands, etc.), you MUST call the appropriate tool. Do NOT just say you'll do it or pretend to do it."
}
func filterNamesByTurnProfile(names []string, allowed []string) []string {
if len(names) == 0 {
return nil
}
allowedSet := cleanAllowedSet(allowed)
if len(allowedSet) == 0 {
return nil
}
out := make([]string, 0, len(names))
for _, name := range names {
if _, ok := allowedSet[strings.ToLower(strings.TrimSpace(name))]; ok {
out = append(out, name)
}
}
return out
}
func filterToolsByTurnProfile(
defs []providers.ToolDefinition,
profile config.EffectiveTurnProfile,
) []providers.ToolDefinition {
if !profile.Enabled {
return defs
}
switch profile.ToolsMode {
case config.TurnProfileModeOff:
return nil
case config.TurnProfileModeCustom:
allowed := cleanAllowedSet(profile.AllowedTools)
if len(allowed) == 0 {
return nil
}
filtered := make([]providers.ToolDefinition, 0, len(defs))
for _, def := range defs {
if _, ok := allowed[strings.ToLower(strings.TrimSpace(def.Function.Name))]; ok {
filtered = append(filtered, def)
}
}
return filtered
default:
return defs
}
}
func cleanAllowedSet(values []string) map[string]struct{} {
if len(values) == 0 {
return nil
}
out := make(map[string]struct{}, len(values))
for _, value := range values {
value = strings.ToLower(strings.TrimSpace(value))
if value == "" {
continue
}
out[value] = struct{}{}
}
return out
}