* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
package copilot
import (
"context"
"errors"
"fmt"
"github.com/github/copilot-sdk/go/rpc"
)
func validateNewClientForMode(opts *ClientOptions) {
if opts == nil || opts.Mode != ModeEmpty {
return
}
if opts.BaseDirectory != "" {
return
}
if opts.SessionFS != nil {
return
}
if _, ok := opts.Connection.(URIConnection); ok {
return
}
panic("Client is in Mode=ModeEmpty but neither BaseDirectory, SessionFS, nor a URIConnection was supplied. " +
"Empty mode requires explicit, per-tenant storage; set ClientOptions.BaseDirectory or .SessionFS, " +
"or connect to an externally-managed runtime via URIConnection.")
}
func validateToolFilterList(field string, list []string) error {
for _, entry := range list {
if entry == "*" {
return fmt.Errorf(
"invalid %s entry %q: there is no bare wildcard. "+
"Use one or more of NewToolSet().AddBuiltIn(\"*\"), .AddMCP(\"*\"), or .AddCustom(\"*\") "+
"to target a specific source",
field, entry)
}
}
return nil
}
func (c *Client) resolveToolFilterOptions(availableTools, excludedTools []string) (
[]string, []string, *rpc.OptionsUpdateToolFilterPrecedence, error,
) {
if err := validateToolFilterList("availableTools", availableTools); err != nil {
return nil, nil, nil, err
}
if err := validateToolFilterList("excludedTools", excludedTools); err != nil {
return nil, nil, nil, err
}
if c.options.Mode == ModeEmpty && availableTools == nil {
return nil, nil, nil, errors.New(
"Client is in Mode=ModeEmpty but the session config did not specify AvailableTools. " +
"Empty mode requires every session to explicitly opt into the tools it wants — " +
"e.g. NewToolSet().AddBuiltIn(BuiltInToolsIsolated...).ToSlice()")
}
precedence := rpc.OptionsUpdateToolFilterPrecedenceExcluded
return availableTools, excludedTools, &precedence, nil
}
func (c *Client) systemMessageForMode(supplied *SystemMessageConfig) *SystemMessageConfig {
if c.options.Mode != ModeEmpty {
return supplied
}
removeAction := SectionOverride{Action: SectionActionRemove}
if supplied == nil {
return &SystemMessageConfig{
Mode: "customize",
Sections: map[string]SectionOverride{"environment_context": removeAction},
}
}
switch supplied.Mode {
case "replace":
return supplied
case "customize":
if _, ok := supplied.Sections["environment_context"]; ok {
return supplied
}
out := *supplied
out.Sections = make(map[string]SectionOverride, len(supplied.Sections)+1)
for k, v := range supplied.Sections {
out.Sections[k] = v
}
out.Sections["environment_context"] = removeAction
return &out
case "append", "":
return &SystemMessageConfig{
Mode: "customize",
Content: supplied.Content,
Sections: map[string]SectionOverride{"environment_context": removeAction},
}
default:
return supplied
}
}
func (c *Client) applyConfigDefaultsForMode(config *SessionConfig) {
if c.options.Mode != ModeEmpty {
return
}
if config.EnableExperimentalMode == nil {
f := false
config.EnableExperimentalMode = &f
}
if config.EnableSessionTelemetry == nil {
f := false
config.EnableSessionTelemetry = &f
}
if config.SkipEmbeddingRetrieval == nil {
t := true
config.SkipEmbeddingRetrieval = &t
}
if config.EmbeddingCacheStorage == nil {
inMemory := "in-memory"
config.EmbeddingCacheStorage = &inMemory
}
if config.EnableOnDemandInstructionDiscovery == nil {
f := false
config.EnableOnDemandInstructionDiscovery = &f
}
if config.EnableFileHooks == nil {
f := false
config.EnableFileHooks = &f
}
if config.EnableHostGitOperations == nil {
f := false
config.EnableHostGitOperations = &f
}
if config.EnableSessionStore == nil {
f := false
config.EnableSessionStore = &f
}
if config.EnableSkills == nil {
f := false
config.EnableSkills = &f
}
if config.Memory == nil {
config.Memory = &MemoryConfiguration{Enabled: false}
}
if config.MCPOAuthTokenStorage == "" {
config.MCPOAuthTokenStorage = "in-memory"
}
if config.CustomAgentsLocalOnly == nil {
localOnly := true
config.CustomAgentsLocalOnly = &localOnly
}
}
func (c *Client) applyResumeDefaultsForMode(config *ResumeSessionConfig) {
if c.options.Mode != ModeEmpty {
return
}
if config.EnableExperimentalMode == nil {
f := false
config.EnableExperimentalMode = &f
}
if config.EnableSessionTelemetry == nil {
f := false
config.EnableSessionTelemetry = &f
}
if config.SkipEmbeddingRetrieval == nil {
t := true
config.SkipEmbeddingRetrieval = &t
}
if config.EmbeddingCacheStorage == nil {
inMemory := "in-memory"
config.EmbeddingCacheStorage = &inMemory
}
if config.EnableOnDemandInstructionDiscovery == nil {
f := false
config.EnableOnDemandInstructionDiscovery = &f
}
if config.EnableFileHooks == nil {
f := false
config.EnableFileHooks = &f
}
if config.EnableHostGitOperations == nil {
f := false
config.EnableHostGitOperations = &f
}
if config.EnableSessionStore == nil {
f := false
config.EnableSessionStore = &f
}
if config.EnableSkills == nil {
f := false
config.EnableSkills = &f
}
if config.Memory == nil {
config.Memory = &MemoryConfiguration{Enabled: false}
}
if config.MCPOAuthTokenStorage == "" {
config.MCPOAuthTokenStorage = "in-memory"
}
if config.CustomAgentsLocalOnly == nil {
localOnly := true
config.CustomAgentsLocalOnly = &localOnly
}
}
func (c *Client) updateSessionOptionsForMode(ctx context.Context, session *Session, base optBackInFields) error {
patch := &rpc.SessionUpdateOptionsParams{}
hasAny := false
if c.options.Mode == ModeEmpty {
if base.SkipCustomInstructions != nil {
patch.SkipCustomInstructions = base.SkipCustomInstructions
} else {
t := true
patch.SkipCustomInstructions = &t
}
if base.CustomAgentsLocalOnly != nil {
patch.CustomAgentsLocalOnly = base.CustomAgentsLocalOnly
} else {
t := true
patch.CustomAgentsLocalOnly = &t
}
if base.CoauthorEnabled != nil {
patch.CoauthorEnabled = base.CoauthorEnabled
} else {
f := false
patch.CoauthorEnabled = &f
}
if base.ManageScheduleEnabled != nil {
patch.ManageScheduleEnabled = base.ManageScheduleEnabled
} else {
f := false
patch.ManageScheduleEnabled = &f
}
patch.InstalledPlugins = []rpc.SessionInstalledPlugin{}
if base.IncludedBuiltinSkills != nil {
patch.IncludedBuiltinSkills = base.IncludedBuiltinSkills
} else {
patch.IncludedBuiltinSkills = []string{}
}
hasAny = true
} else {
if base.SkipCustomInstructions != nil {
patch.SkipCustomInstructions = base.SkipCustomInstructions
hasAny = true
}
if base.CustomAgentsLocalOnly != nil {
patch.CustomAgentsLocalOnly = base.CustomAgentsLocalOnly
hasAny = true
}
if base.CoauthorEnabled != nil {
patch.CoauthorEnabled = base.CoauthorEnabled
hasAny = true
}
if base.ManageScheduleEnabled != nil {
patch.ManageScheduleEnabled = base.ManageScheduleEnabled
hasAny = true
}
if base.IncludedBuiltinSkills != nil {
patch.IncludedBuiltinSkills = base.IncludedBuiltinSkills
hasAny = true
}
}
if !hasAny {
return nil
}
if _, err := session.RPC.Options.Update(ctx, patch); err != nil {
_ = session.Disconnect()
c.sessionsMux.Lock()
delete(c.sessions, session.SessionID)
c.sessionsMux.Unlock()
return fmt.Errorf("failed to apply mode-specific session options: %w", err)
}
return nil
}
type optBackInFields struct {
SkipCustomInstructions *bool
CustomAgentsLocalOnly *bool
CoauthorEnabled *bool
ManageScheduleEnabled *bool
IncludedBuiltinSkills []string
}