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
This commit is contained in:
lxowalle
2026-05-22 10:06:40 +08:00
committed by GitHub
parent 5bbebb5fc8
commit 2992eccbf0
39 changed files with 3150 additions and 162 deletions
+19 -13
View File
@@ -79,17 +79,18 @@ type AgentLoop struct {
// processOptions configures how a message is processed
type processOptions struct {
Dispatch DispatchRequest // Normalized routed request boundary for this turn
SessionKey string // Session identifier for history/context
SessionAliases []string // Compatibility aliases for the session key
Channel string // Target channel for tool execution
ChatID string // Target chat ID for tool execution
MessageID string // Current inbound platform message ID
ReplyToMessageID string // Current inbound reply target message ID
SenderID string // Current sender ID for dynamic context
SenderDisplayName string // Current sender display name for dynamic context
UserMessage string // User message content (may include prefix)
ForcedSkills []string // Skills explicitly requested for this message
Dispatch DispatchRequest // Normalized routed request boundary for this turn
SessionKey string // Session identifier for history/context
SessionAliases []string // Compatibility aliases for the session key
Channel string // Target channel for tool execution
ChatID string // Target chat ID for tool execution
MessageID string // Current inbound platform message ID
ReplyToMessageID string // Current inbound reply target message ID
SenderID string // Current sender ID for dynamic context
SenderDisplayName string // Current sender display name for dynamic context
UserMessage string // User message content (may include prefix)
ForcedSkills []string // Skills explicitly requested for this message
TurnProfile config.EffectiveTurnProfile
SystemPromptOverride string // Override the default system prompt (Used by SubTurns)
Media []string // media:// refs from inbound message
InitialSteeringMessages []providers.Message // Steering messages from refactor/agent
@@ -534,17 +535,22 @@ func (al *AgentLoop) runAgentLoop(
opts processOptions,
) (string, error) {
opts = normalizeProcessOptions(opts)
var err error
opts, err = resolveTurnProfileOptions(al.GetConfig(), opts)
if err != nil {
return "", err
}
// Record last channel for heartbeat notifications (skip internal channels and cli)
if opts.Dispatch.Channel() != "" &&
opts.Dispatch.ChatID() != "" &&
!constants.IsInternalChannel(opts.Dispatch.Channel()) {
channelKey := fmt.Sprintf("%s:%s", opts.Dispatch.Channel(), opts.Dispatch.ChatID())
if err := al.RecordLastChannel(channelKey); err != nil {
if recordErr := al.RecordLastChannel(channelKey); recordErr != nil {
logger.WarnCF(
"agent",
"Failed to record last channel",
map[string]any{"error": err.Error()},
map[string]any{"error": recordErr.Error()},
)
}
}