diff --git a/cagent-schema.json b/cagent-schema.json index 4ba3233a..2fdccd17 100644 --- a/cagent-schema.json +++ b/cagent-schema.json @@ -8,8 +8,8 @@ "version": { "type": "string", "description": "Configuration version", - "enum": ["2"], - "examples": ["2"] + "enum": ["1", "2", "v1", "v2"], + "examples": ["1", "2", "v1", "v2"] }, "agents": { "type": "object", @@ -93,6 +93,22 @@ "items": { "type": "string" } + }, + "commands": { + "description": "Named prompts for quick-start commands used with --command/-c", + "oneOf": [ + { + "type": "object", + "additionalProperties": { "type": "string" } + }, + { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { "type": "string" } + } + } + ] } }, "additionalProperties": false diff --git a/cmd/root/run.go b/cmd/root/run.go index 52102c8f..db2dbf91 100644 --- a/cmd/root/run.go +++ b/cmd/root/run.go @@ -11,6 +11,7 @@ import ( "os" "os/signal" "path/filepath" + "sort" "strings" "time" @@ -21,6 +22,7 @@ import ( "github.com/docker/cagent/pkg/app" "github.com/docker/cagent/pkg/chat" + "github.com/docker/cagent/pkg/config" "github.com/docker/cagent/pkg/content" "github.com/docker/cagent/pkg/evaluation" "github.com/docker/cagent/pkg/remote" @@ -40,8 +42,11 @@ var ( useTUI bool remoteAddress string dryRun bool + commandName string ) +const commandListSentinel = "__LIST__" + // NewRunCmd creates a new run command func NewRunCmd() *cobra.Command { cmd := &cobra.Command{ @@ -64,6 +69,11 @@ func NewRunCmd() *cobra.Command { cmd.PersistentFlags().StringVar(&attachmentPath, "attach", "", "Attach an image file to the message") cmd.PersistentFlags().BoolVar(&useTUI, "tui", true, "Run the agent with a Terminal User Interface (TUI)") cmd.PersistentFlags().StringVar(&remoteAddress, "remote", "", "Use remote runtime with specified address (only supported with TUI)") + cmd.PersistentFlags().StringVarP(&commandName, "command", "c", "", "Run a named command from the agent's commands section") + if f := cmd.PersistentFlags().Lookup("command"); f != nil { + // Allow `-c` without value to list available commands + f.NoOptDefVal = commandListSentinel + } addGatewayFlags(cmd) return cmd @@ -200,6 +210,52 @@ func doRunCommand(ctx context.Context, args []string, exec bool) error { slog.Debug("Skipping local agent file loading for remote runtime", "filename", agentFilename) } + // Resolve --command/-c into a first message if provided + var commandFirstMessage *string + if trimmed := strings.TrimSpace(commandName); trimmed != "" { + // Handle listing commands when -c is provided without a value + if trimmed == commandListSentinel { + // If the next positional arg looks like a value (not a flag), treat it as the command value. + if len(args) == 2 && !strings.HasPrefix(args[1], "-") { + trimmed = args[1] + // consume the positional so it won't be treated as a message later + args = args[:1] + } else { + cmds, err := getCommandsForAgent(agentFilename, remoteAddress != "", agents, agentName) + if err != nil { + return err + } + if len(cmds) == 0 { + return fmt.Errorf("No commands defined for agent '%s'.", agentName) + } + printAvailableCommands(agentName, cmds) + fmt.Println() + return nil + } + } + + if len(args) == 2 { + return fmt.Errorf("cannot use --command (-c) together with a message argument") + } + + cmds, err := getCommandsForAgent(agentFilename, remoteAddress != "", agents, agentName) + if err != nil { + return err + } + if len(cmds) == 0 { + return fmt.Errorf("agent '%s' has no commands", agentName) + } + if msg, ok := cmds[trimmed]; ok { + commandFirstMessage = &msg + } else { + var names []string + for k := range cmds { + names = append(names, k) + } + return fmt.Errorf("'%s' is an unknown command.\n\nAvailable: %s", trimmed, strings.Join(names, ", ")) + } + } + // Validate remote flag usage if remoteAddress != "" && (!useTUI || exec) { return fmt.Errorf("--remote flag can only be used with TUI mode") @@ -267,6 +323,10 @@ func doRunCommand(ctx context.Context, args []string, exec bool) error { // For `cagent run --tui=false` if !useTUI { + // Inject first message for non-TUI if --command was used + if commandFirstMessage != nil { + args = []string{args[0], *commandFirstMessage} + } return runWithoutTUI(ctx, agentFilename, rt, sess, args) } @@ -286,6 +346,11 @@ func doRunCommand(ctx context.Context, args []string, exec bool) error { } } + // Override firstMessage if --command was provided (cannot be combined with a message arg) + if commandFirstMessage != nil { + firstMessage = commandFirstMessage + } + a := app.New("cagent", agentFilename, rt, agents, sess, firstMessage) m := tui.New(a) @@ -767,3 +832,50 @@ func fileToDataURL(filePath string) (string, error) { return dataURL, nil } + +// getCommandsForAgent returns the commands map for the selected agent, +// loading from the in-memory team for local runs or from the YAML file for remote runs. +func getCommandsForAgent(agentFilename string, isRemote bool, agents *team.Team, agentName string) (map[string]string, error) { + if !isRemote { + if agents == nil { + return nil, fmt.Errorf("failed to load agent team") + } + ag := agents.Agent(agentName) + if ag == nil { + return nil, fmt.Errorf("agent not found: %s", agentName) + } + return ag.Commands(), nil + } + + parentDir := filepath.Dir(agentFilename) + fileName := filepath.Base(agentFilename) + root, err := os.OpenRoot(parentDir) + if err != nil { + return nil, fmt.Errorf("failed to open root: %w", err) + } + defer func() { + if err := root.Close(); err != nil { + slog.Error("Failed to close root", "error", err) + } + }() + + cfg, err := config.LoadConfig(fileName, root) + if err != nil { + return nil, fmt.Errorf("failed to load agent config: %w", err) + } + + return map[string]string(cfg.Agents[agentName].Commands), nil +} + +// printAvailableCommands pretty-prints the agent's commands sorted by name. +func printAvailableCommands(agentName string, cmds map[string]string) { + fmt.Printf("Available commands for agent '%s':\n", agentName) + var names []string + for k := range cmds { + names = append(names, k) + } + sort.Strings(names) + for _, n := range names { + fmt.Printf(" - %s: %s\n", n, cmds[n]) + } +} diff --git a/docs/USAGE.md b/docs/USAGE.md index c17d81c2..0cd3c85d 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -41,6 +41,7 @@ $ cagent run config.yaml -a agent_name # Run a specific agent $ cagent run config.yaml --debug # Enable debug logging $ cagent run config.yaml --yolo # Auto-accept all the tool calls $ cagent run config.yaml "First message" # Start the conversation with the agent with a first message +$ cagent run config.yaml -c df # Run with a named command from YAML # One off without TUI $ cagent exec config.yaml # Run the agent once, with default instructions @@ -76,17 +77,18 @@ During CLI sessions, you can use special commands: ### Agent Properties -| Property | Type | Description | Required | -|------------------------|---------|-----------------------------------------------------------------|----------| -| `name` | string | Agent identifier | ✓ | -| `model` | string | Model reference | ✓ | -| `description` | string | Agent purpose | ✓ | -| `instruction` | string | Detailed behavior instructions | ✓ | -| `sub_agents` | array | List of sub-agent names | ✗ | -| `toolsets` | array | Available tools | ✗ | -| `add_date` | boolean | Add current date to context | ✗ | -| `add_environment_info` | boolean | Add information about the environment (working dir, OS, git...) | ✗ | -| `max_iterations` | int | Specifies how many times the agent can loop when using tools | ✗ | +| Property | Type | Description | Required | +|------------------------|--------------|-----------------------------------------------------------------|----------| +| `name` | string | Agent identifier | ✓ | +| `model` | string | Model reference | ✓ | +| `description` | string | Agent purpose | ✓ | +| `instruction` | string | Detailed behavior instructions | ✓ | +| `sub_agents` | array | List of sub-agent names | ✗ | +| `toolsets` | array | Available tools | ✗ | +| `add_date` | boolean | Add current date to context | ✗ | +| `add_environment_info` | boolean | Add information about the environment (working dir, OS, git...) | ✗ | +| `max_iterations` | int | Specifies how many times the agent can loop when using tools | ✗ | +| `commands` | object/array | Named prompts for quick-start commands (used with `--command`) | ✗ | #### Example @@ -101,6 +103,33 @@ agents: add_date: boolean # Add current date to context (optional) add_environment_info: boolean # Add information about the environment (working dir, OS, git...) (optional) max_iterations: int # How many times this agent can loop when calling tools (optional, default = unlimited) + commands: # Either mapping or list of singleton maps + df: "check how much free space i have on my disk" + ls: "list the files in the current directory" +``` + +### Running with named commands + +- Use `--command` (or `-c`) to send a predefined prompt from the agent config as the first message. +- Example YAML forms supported: + +```yaml +commands: + df: "check how much free space i have on my disk" + ls: "list the files in the current directory" +``` + +```yaml +commands: + - df: "check how much free space i have on my disk" + - ls: "list the files in the current directory" +``` + +Run: + +```bash +cagent run ./agent.yaml -c df +cagent run ./agent.yaml --command ls ``` ### Model Properties diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 67cedd31..32e7c51e 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -30,6 +30,7 @@ type Agent struct { addPromptFiles []string toolWrapper toolWrapper memoryManager memorymanager.Manager + commands map[string]string } // New creates a new agent @@ -145,6 +146,11 @@ func (a *Agent) ToolSets() []tools.ToolSet { return a.toolsets } +// Commands returns the named commands configured for this agent. +func (a *Agent) Commands() map[string]string { + return a.commands +} + func (a *Agent) ensureToolSetsAreStarted() error { a.toolsetsMutex.Lock() defer a.toolsetsMutex.Unlock() diff --git a/pkg/agent/opts.go b/pkg/agent/opts.go index 38e98c0c..e56419e3 100644 --- a/pkg/agent/opts.go +++ b/pkg/agent/opts.go @@ -90,3 +90,9 @@ func WithNumHistoryItems(numHistoryItems int) Opt { a.numHistoryItems = numHistoryItems } } + +func WithCommands(commands map[string]string) Opt { + return func(a *Agent) { + a.commands = commands + } +} diff --git a/pkg/config/commands_test.go b/pkg/config/commands_test.go new file mode 100644 index 00000000..3e9da1a5 --- /dev/null +++ b/pkg/config/commands_test.go @@ -0,0 +1,52 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestV2Commands_AllForms(t *testing.T) { + cfg, err := LoadConfig("commands_v2.yaml", openRoot(t, "testdata")) + require.NoError(t, err) + // map form + cmdsMap := cfg.Agents["root"].Commands + require.Equal(t, "check disk", cmdsMap["df"]) + require.Equal(t, "list files", cmdsMap["ls"]) + // list form + cmdsList := cfg.Agents["another_agent"].Commands + require.Equal(t, "check disk", cmdsList["df"]) + require.Equal(t, "list files", cmdsList["ls"]) + // none + require.Empty(t, cfg.Agents["none_agent"].Commands) +} + +func TestMigrate_v1_Commands_AllForms(t *testing.T) { + cfg, err := LoadConfig("commands_v1.yaml", openRoot(t, "testdata")) + require.NoError(t, err) + // map form + cmdsMap := cfg.Agents["root"].Commands + require.Equal(t, "check disk", cmdsMap["df"]) + require.Equal(t, "list files", cmdsMap["ls"]) + // list form + cmdsList := cfg.Agents["another_agent"].Commands + require.Equal(t, "check disk", cmdsList["df"]) + require.Equal(t, "list files", cmdsList["ls"]) + // none + require.Empty(t, cfg.Agents["yet_another_agent"].Commands) +} + +func TestMigrate_v0_Commands_AllForms(t *testing.T) { + cfg, err := LoadConfig("commands_v0.yaml", openRoot(t, "testdata")) + require.NoError(t, err) + // map form + cmdsMap := cfg.Agents["root"].Commands + require.Equal(t, "check disk", cmdsMap["df"]) + require.Equal(t, "list files", cmdsMap["ls"]) + // list form + cmdsList := cfg.Agents["another_agent"].Commands + require.Equal(t, "check disk", cmdsList["df"]) + require.Equal(t, "list files", cmdsList["ls"]) + // none + require.Empty(t, cfg.Agents["yet_another_agent"].Commands) +} diff --git a/pkg/config/testdata/commands_v0.yaml b/pkg/config/testdata/commands_v0.yaml new file mode 100644 index 00000000..54a08503 --- /dev/null +++ b/pkg/config/testdata/commands_v0.yaml @@ -0,0 +1,19 @@ +agents: + + root: + model: openai/gpt-4o + instruction: you are a helpful computer assistant + commands: + df: "check disk" + ls: "list files" + + another_agent: + model: openai/gpt-4o + instruction: you are a helpful computer assistant + commands: + - df: "check disk" + - ls: "list files" + + yet_another_agent: + model: openai/gpt-4o + instruction: you are a helpful computer assistant diff --git a/pkg/config/testdata/commands_v1.yaml b/pkg/config/testdata/commands_v1.yaml new file mode 100644 index 00000000..616457cd --- /dev/null +++ b/pkg/config/testdata/commands_v1.yaml @@ -0,0 +1,21 @@ +version: "1" + +agents: + + root: + model: openai/gpt-4o + instruction: you are a helpful computer assistant + commands: + df: "check disk" + ls: "list files" + + another_agent: + model: openai/gpt-4o + instruction: you are a helpful computer assistant + commands: + - df: "check disk" + - ls: "list files" + + yet_another_agent: + model: openai/gpt-4o + instruction: you are a helpful computer assistant diff --git a/pkg/config/testdata/commands_v2.yaml b/pkg/config/testdata/commands_v2.yaml new file mode 100644 index 00000000..59b4bbbb --- /dev/null +++ b/pkg/config/testdata/commands_v2.yaml @@ -0,0 +1,21 @@ +version: "2" + +agents: + + root: + model: openai/gpt-4o + instruction: you are a helpful computer assistant + commands: + df: "check disk" + ls: "list files" + + another_agent: + model: openai/gpt-4o + instruction: you are a helpful computer assistant + commands: + - df: "check disk" + - ls: "list files" + + yet_another_agent: + model: openai/gpt-4o + instruction: you are a helpful computer assistant diff --git a/pkg/config/types/commands.go b/pkg/config/types/commands.go new file mode 100644 index 00000000..46dedb71 --- /dev/null +++ b/pkg/config/types/commands.go @@ -0,0 +1,43 @@ +package types + +// Commands represents a set of named prompts for quick-starting conversations. +// It supports two YAML formats: +// +// commands: +// +// df: "check disk space" +// ls: "list files" +// +// or +// +// commands: +// - df: "check disk space" +// - ls: "list files" +type Commands map[string]string + +// UnmarshalYAML supports both map and list-of-singleton-maps syntaxes. +func (c *Commands) UnmarshalYAML(unmarshal func(any) error) error { + // Try direct map first + var m map[string]string + if err := unmarshal(&m); err == nil && m != nil { + *c = m + return nil + } + + // Try list of maps [{k:v}, {k:v}] + var list []map[string]string + if err := unmarshal(&list); err == nil && list != nil { + result := make(map[string]string) + for _, item := range list { + for k, v := range item { + result[k] = v + } + } + *c = result + return nil + } + + // If none matched, treat as empty + *c = map[string]string{} + return nil +} diff --git a/pkg/config/v0/types.go b/pkg/config/v0/types.go index 45efb9e1..d967af26 100644 --- a/pkg/config/v0/types.go +++ b/pkg/config/v0/types.go @@ -77,17 +77,18 @@ func (t *TodoConfig) UnmarshalYAML(unmarshal func(any) error) error { // AgentConfig represents a single agent configuration type AgentConfig struct { - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Model string `json:"model,omitempty" yaml:"model,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Toolsets []Toolset `json:"toolsets,omitempty" yaml:"toolsets,omitempty"` - Instruction string `json:"instruction,omitempty" yaml:"instruction,omitempty"` - SubAgents []string `json:"sub_agents,omitempty" yaml:"sub_agents,omitempty"` - AddDate bool `json:"add_date,omitempty" yaml:"add_date,omitempty"` - Think bool `json:"think,omitempty" yaml:"think,omitempty"` - Todo TodoConfig `json:"todo,omitempty" yaml:"todo,omitempty"` - MemoryConfig MemoryConfig `json:"memory,omitempty" yaml:"memory,omitempty"` - NumHistoryItems int `json:"num_history_items,omitempty" yaml:"num_history_items,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Model string `json:"model,omitempty" yaml:"model,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Toolsets []Toolset `json:"toolsets,omitempty" yaml:"toolsets,omitempty"` + Instruction string `json:"instruction,omitempty" yaml:"instruction,omitempty"` + SubAgents []string `json:"sub_agents,omitempty" yaml:"sub_agents,omitempty"` + AddDate bool `json:"add_date,omitempty" yaml:"add_date,omitempty"` + Think bool `json:"think,omitempty" yaml:"think,omitempty"` + Todo TodoConfig `json:"todo,omitempty" yaml:"todo,omitempty"` + MemoryConfig MemoryConfig `json:"memory,omitempty" yaml:"memory,omitempty"` + NumHistoryItems int `json:"num_history_items,omitempty" yaml:"num_history_items,omitempty"` + Commands types.Commands `json:"commands,omitempty" yaml:"commands,omitempty"` } type MemoryConfig struct { diff --git a/pkg/config/v1/types.go b/pkg/config/v1/types.go index bb0db6a1..939a449e 100644 --- a/pkg/config/v1/types.go +++ b/pkg/config/v1/types.go @@ -103,14 +103,15 @@ func (t *Toolset) UnmarshalYAML(unmarshal func(any) error) error { // AgentConfig represents a single agent configuration type AgentConfig struct { - Model string `json:"model,omitempty" yaml:"model,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Toolsets []Toolset `json:"toolsets,omitempty" yaml:"toolsets,omitempty"` - Instruction string `json:"instruction,omitempty" yaml:"instruction,omitempty"` - SubAgents []string `json:"sub_agents,omitempty" yaml:"sub_agents,omitempty"` - AddDate bool `json:"add_date,omitempty" yaml:"add_date,omitempty"` - AddEnvironmentInfo bool `json:"add_environment_info,omitempty" yaml:"add_environment_info,omitempty"` - NumHistoryItems int `json:"num_history_items,omitempty" yaml:"num_history_items,omitempty"` + Model string `json:"model,omitempty" yaml:"model,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Toolsets []Toolset `json:"toolsets,omitempty" yaml:"toolsets,omitempty"` + Instruction string `json:"instruction,omitempty" yaml:"instruction,omitempty"` + SubAgents []string `json:"sub_agents,omitempty" yaml:"sub_agents,omitempty"` + AddDate bool `json:"add_date,omitempty" yaml:"add_date,omitempty"` + AddEnvironmentInfo bool `json:"add_environment_info,omitempty" yaml:"add_environment_info,omitempty"` + NumHistoryItems int `json:"num_history_items,omitempty" yaml:"num_history_items,omitempty"` + Commands types.Commands `json:"commands,omitempty" yaml:"commands,omitempty"` } // ModelConfig represents the configuration for a model diff --git a/pkg/config/v2/types.go b/pkg/config/v2/types.go index d4a681ec..c5f60572 100644 --- a/pkg/config/v2/types.go +++ b/pkg/config/v2/types.go @@ -1,5 +1,9 @@ package v2 +import ( + "github.com/docker/cagent/pkg/config/types" +) + // Config represents the entire configuration file type Config struct { Version string `json:"version,omitempty"` @@ -10,17 +14,18 @@ type Config struct { // AgentConfig represents a single agent configuration type AgentConfig struct { - Model string `json:"model,omitempty"` - Description string `json:"description,omitempty"` - Toolsets []Toolset `json:"toolsets,omitempty"` - Instruction string `json:"instruction,omitempty"` - SubAgents []string `json:"sub_agents,omitempty"` - AddDate bool `json:"add_date,omitempty"` - AddEnvironmentInfo bool `json:"add_environment_info,omitempty"` - CodeModeTools bool `json:"code_mode_tools,omitempty"` - MaxIterations int `json:"max_iterations,omitempty"` - NumHistoryItems int `json:"num_history_items,omitempty"` - AddPromptFiles []string `json:"add_prompt_files,omitempty" yaml:"add_prompt_files,omitempty"` + Model string `json:"model,omitempty"` + Description string `json:"description,omitempty"` + Toolsets []Toolset `json:"toolsets,omitempty"` + Instruction string `json:"instruction,omitempty"` + SubAgents []string `json:"sub_agents,omitempty"` + AddDate bool `json:"add_date,omitempty"` + AddEnvironmentInfo bool `json:"add_environment_info,omitempty"` + CodeModeTools bool `json:"code_mode_tools,omitempty"` + MaxIterations int `json:"max_iterations,omitempty"` + NumHistoryItems int `json:"num_history_items,omitempty"` + AddPromptFiles []string `json:"add_prompt_files,omitempty" yaml:"add_prompt_files,omitempty"` + Commands types.Commands `json:"commands,omitempty"` } // ModelConfig represents the configuration for a model @@ -46,6 +51,21 @@ type Metadata struct { Readme string `json:"readme,omitempty"` } +// Commands represents a set of named prompts for quick-starting conversations. +// It supports two YAML formats: +// +// commands: +// +// df: "check disk space" +// ls: "list files" +// +// or +// +// commands: +// - df: "check disk space" +// - ls: "list files" +// Commands YAML unmarshalling is implemented in pkg/config/types/commands.go + // ScriptShellToolConfig represents a custom shell tool configuration type ScriptShellToolConfig struct { Cmd string `json:"cmd"` diff --git a/pkg/config/v2/types_test.go b/pkg/config/v2/types_test.go new file mode 100644 index 00000000..08d31f96 --- /dev/null +++ b/pkg/config/v2/types_test.go @@ -0,0 +1,34 @@ +package v2 + +import ( + "testing" + + "github.com/goccy/go-yaml" + "github.com/stretchr/testify/require" + + "github.com/docker/cagent/pkg/config/types" +) + +func TestCommandsUnmarshal_Map(t *testing.T) { + var c types.Commands + input := []byte(` +df: "check disk" +ls: "list files" +`) + err := yaml.Unmarshal(input, &c) + require.NoError(t, err) + require.Equal(t, "check disk", c["df"]) + require.Equal(t, "list files", c["ls"]) +} + +func TestCommandsUnmarshal_List(t *testing.T) { + var c types.Commands + input := []byte(` +- df: "check disk" +- ls: "list files" +`) + err := yaml.Unmarshal(input, &c) + require.NoError(t, err) + require.Equal(t, "check disk", c["df"]) + require.Equal(t, "list files", c["ls"]) +} diff --git a/pkg/server/server.go b/pkg/server/server.go index 00227716..bf9805c9 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -1039,6 +1039,32 @@ func (s *Server) runAgent(c echo.Context) error { // TODO(dga): for now, we only receive one message and it's always a user message. for _, msg := range messages { sess.AddMessage(session.UserMessage(agentFilename, msg.Content)) + // Optional: run using a named command (?command=name) + if cmd := strings.TrimSpace(c.QueryParam("command")); cmd != "" { + cmds := agent.Commands() + if len(cmds) == 0 { + return c.JSON(http.StatusBadRequest, map[string]string{"error": fmt.Sprintf("agent '%s' has no commands", currentAgent)}) + } + text, ok := cmds[cmd] + if !ok { + var names []string + for k := range cmds { + names = append(names, k) + } + sort.Strings(names) + return c.JSON(http.StatusBadRequest, map[string]string{"error": fmt.Sprintf("unknown command '%s'. Available: %s", cmd, strings.Join(names, ", "))}) + } + sess.AddMessage(session.UserMessage(agentFilename, text)) + } else { + var messages []api.Message + if err := json.NewDecoder(c.Request().Body).Decode(&messages); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"}) + } + // TODO(dga): for now, we only receive one message and it's always a user message. + for _, msg := range messages { + sess.AddMessage(session.UserMessage(agentFilename, msg.Content)) + } + } } if err := s.sessionStore.UpdateSession(c.Request().Context(), sess); err != nil { diff --git a/pkg/teamloader/teamloader.go b/pkg/teamloader/teamloader.go index 6f9e9092..e8f7fb0d 100644 --- a/pkg/teamloader/teamloader.go +++ b/pkg/teamloader/teamloader.go @@ -152,6 +152,7 @@ func Load(ctx context.Context, path string, runtimeConfig config.RuntimeConfig) agent.WithAddPromptFiles(agentConfig.AddPromptFiles), agent.WithMaxIterations(agentConfig.MaxIterations), agent.WithNumHistoryItems(agentConfig.NumHistoryItems), + agent.WithCommands(map[string]string(agentConfig.Commands)), } for _, model := range models { opts = append(opts, agent.WithModel(model))