| 415 | } |
| 416 | |
| 417 | func (s *Server) runAgent(c echo.Context) error { |
| 418 | sessionID := c.Param("id") |
| 419 | agentFilename := c.Param("agent") |
| 420 | // agent_name may be empty when the route /api/sessions/:id/agent/:agent |
| 421 | // is used. In that case, the session manager resolves the team's default |
| 422 | // agent (one explicitly named "root" if it exists, otherwise the first |
| 423 | // agent declared). |
| 424 | currentAgent := c.Param("agent_name") |
| 425 | |
| 426 | slog.Debug("Running agent", "agent_filename", agentFilename, "session_id", sessionID, "current_agent", currentAgent) |
| 427 | |
| 428 | var req api.RunAgentRequest |
| 429 | if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil { |
| 430 | return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid request body: %v", err)) |
| 431 | } |
| 432 | |
| 433 | streamChan, err := s.sm.RunSession(c.Request().Context(), sessionID, agentFilename, currentAgent, req.Messages, req.Model) |
| 434 | if err != nil { |
| 435 | if errors.Is(err, ErrSessionBusy) { |
| 436 | return echo.NewHTTPError(http.StatusConflict, err.Error()) |
| 437 | } |
| 438 | if errors.Is(err, ErrModelSwitchingNotSupported) { |
| 439 | return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error()) |
| 440 | } |
| 441 | return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to run session: %v", err)) |
| 442 | } |
| 443 | |
| 444 | c.Response().Header().Set("Content-Type", "text/event-stream") |
| 445 | c.Response().Header().Set("Cache-Control", "no-cache") |
| 446 | c.Response().Header().Set("Connection", "keep-alive") |
| 447 | c.Response().WriteHeader(http.StatusOK) |
| 448 | for { |
| 449 | select { |
| 450 | case event, ok := <-streamChan: |
| 451 | if !ok { |
| 452 | return nil |
| 453 | } |
| 454 | data, err := json.Marshal(event) |
| 455 | if err != nil { |
| 456 | return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to marshal event: %v", err)) |
| 457 | } |
| 458 | fmt.Fprintf(c.Response(), "data: %s\n\n", string(data)) |
| 459 | c.Response().Flush() |
| 460 | case <-c.Request().Context().Done(): |
| 461 | slog.DebugContext(c.Request().Context(), "Client disconnected from stream", "session_id", sessionID) |
| 462 | return nil |
| 463 | } |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | func (s *Server) elicitation(c echo.Context) error { |
| 468 | sessionID := c.Param("id") |